]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #14901 from w-simon/fix-tests
authorChris Down <chris@chrisdown.name>
Wed, 11 Mar 2020 15:01:20 +0000 (15:01 +0000)
committerGitHub <noreply@github.com>
Wed, 11 Mar 2020 15:01:20 +0000 (15:01 +0000)
test: fix some failures in test-cgroup

762 files changed:
.github/FUNDING.yml
.lgtm.yml
.mailmap
.mkosi/mkosi.fedora
NEWS
TODO
catalog/systemd.hr.catalog.in
configure
docs/BOOT_LOADER_INTERFACE.md
docs/BOOT_LOADER_SPECIFICATION.md
docs/CGROUP_DELEGATION.md
docs/CODE_OF_CONDUCT.md
docs/CONTAINER_INTERFACE.md
docs/DISCOVERABLE_PARTITIONS.md [new file with mode: 0644]
docs/ENVIRONMENT.md
docs/GROUP_RECORD.md [new file with mode: 0644]
docs/HACKING.md
docs/HOME_DIRECTORY.md [new file with mode: 0644]
docs/INITRD_INTERFACE.md
docs/PORTABILITY_AND_STABILITY.md
docs/PORTABLE_SERVICES.md
docs/ROOT_STORAGE_DAEMONS.md
docs/SECURITY.md
docs/TESTING_WITH_SANITIZERS.md
docs/TRANSIENT-SETTINGS.md
docs/UIDS-GIDS.md
docs/USER_GROUP_API.md [new file with mode: 0644]
docs/USER_RECORD.md [new file with mode: 0644]
docs/_includes/footer.html
docs/index.md
factory/etc/nsswitch.conf
factory/etc/pam.d/system-auth
fuzzbuzz.yaml
hwdb.d/20-OUI.hwdb
hwdb.d/20-acpi-vendor.hwdb
hwdb.d/20-acpi-vendor.hwdb.patch
hwdb.d/20-pci-vendor-model.hwdb
hwdb.d/20-usb-vendor-model.hwdb
hwdb.d/60-keyboard.hwdb
hwdb.d/60-sensor.hwdb
hwdb.d/acpi_id_registry.html
hwdb.d/ma-large.txt
hwdb.d/ma-medium.txt
hwdb.d/ma-small.txt
hwdb.d/meson.build
hwdb.d/parse_hwdb.py
hwdb.d/pci.ids
hwdb.d/usb.ids
man/bootup.xml
man/environment.d.xml
man/halt.xml
man/homectl.xml [new file with mode: 0644]
man/journalctl.xml
man/journald.conf.xml
man/networkctl.xml
man/networkd.conf.xml
man/nss-systemd.xml
man/pam_systemd.xml
man/pam_systemd_home.xml [new file with mode: 0644]
man/portablectl.xml
man/repart.d.xml [new file with mode: 0644]
man/resolved.conf.xml
man/rules/meson.build
man/sd-bus.xml
man/sd_bus_enqueue_for_read.xml [new file with mode: 0644]
man/sd_bus_message_dump.xml [new file with mode: 0644]
man/sd_bus_message_read.xml
man/sd_bus_message_read_array.xml
man/sd_bus_message_read_basic.xml
man/sd_journal_open.xml
man/standard-conf.xml
man/supported-controllers.xml [new file with mode: 0644]
man/sysctl.d.xml
man/systemctl.xml
man/systemd-bless-boot.service.xml
man/systemd-boot-check-no-failures.service.xml
man/systemd-fstab-generator.xml
man/systemd-gpt-auto-generator.xml
man/systemd-homed.service.xml [new file with mode: 0644]
man/systemd-id128.xml
man/systemd-journald.service.xml
man/systemd-makefs@.service.xml
man/systemd-network-generator.service.xml [new file with mode: 0644]
man/systemd-networkd-wait-online.service.xml
man/systemd-networkd.service.xml
man/systemd-nspawn.xml
man/systemd-pstore.service.xml [moved from man/systemd-pstore.xml with 89% similarity]
man/systemd-repart.xml [new file with mode: 0644]
man/systemd-system.conf.xml
man/systemd-userdbd.service.xml [new file with mode: 0644]
man/systemd.exec.xml
man/systemd.journal-fields.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.path.xml
man/systemd.resource-control.xml
man/systemd.scope.xml
man/systemd.special.xml
man/systemd.syntax.xml
man/systemd.timer.xml
man/systemd.unit.xml
man/systemd.xml
man/sysusers.d.xml
man/tmpfiles.d.xml
man/udev.xml
man/user@.service.xml
man/userdbctl.xml [new file with mode: 0644]
meson.build
meson_options.txt
po/POTFILES.in
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/60-evdev.rules
semaphoreci/semaphore-runner.sh
shell-completion/bash/portablectl
src/analyze/analyze-security.c
src/analyze/analyze.c
src/basic/efivars.c
src/basic/efivars.h
src/basic/escape.c
src/basic/escape.h
src/basic/extract-word.c
src/basic/format-util.h
src/basic/fs-util.c
src/basic/fs-util.h
src/basic/linux/can/netlink.h [new file with mode: 0644]
src/basic/linux/update.sh
src/basic/locale-util.c
src/basic/locale-util.h
src/basic/nss-util.h
src/basic/parse-util.c
src/basic/parse-util.h
src/basic/path-util.c
src/basic/path-util.h
src/basic/proc-cmdline.c
src/basic/process-util.h
src/basic/selinux-util.c
src/basic/selinux-util.h
src/basic/string-table.h
src/basic/string-util.c
src/basic/strv.c
src/basic/strv.h
src/basic/syslog-util.c
src/basic/syslog-util.h
src/basic/user-util.c
src/basic/user-util.h
src/basic/virt.c
src/boot/efi/boot.c
src/boot/efi/meson.build
src/boot/efi/stub.c
src/boot/efi/util.c
src/boot/efi/util.h
src/busctl/busctl.c
src/core/automount.c
src/core/cgroup.c
src/core/core-varlink.c [new file with mode: 0644]
src/core/core-varlink.h [new file with mode: 0644]
src/core/dbus-execute.c
src/core/dbus-job.c
src/core/dbus-manager.c
src/core/dbus-unit.c
src/core/dbus-util.h
src/core/dbus.c
src/core/dbus.h
src/core/device.c
src/core/dynamic-user.c
src/core/execute.c
src/core/execute.h
src/core/job.c
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/load-fragment.h
src/core/main.c
src/core/manager.c
src/core/manager.h
src/core/meson.build
src/core/mount-setup.c
src/core/mount.c
src/core/namespace.c
src/core/namespace.h
src/core/path.c
src/core/scope.c
src/core/selinux-access.c
src/core/selinux-access.h
src/core/service.c
src/core/show-status.c
src/core/show-status.h
src/core/socket.c
src/core/swap.c
src/core/systemd.pc.in
src/core/timer.c
src/core/transaction.c
src/core/unit.c
src/core/unit.h
src/cryptsetup/cryptsetup-generator.c
src/dissect/dissect.c
src/firstboot/firstboot.c
src/fstab-generator/fstab-generator.c
src/fuzz/fuzz-bus-message.c
src/gpt-auto-generator/gpt-auto-generator.c
src/hibernate-resume/hibernate-resume-generator.c
src/home/home-util.c [new file with mode: 0644]
src/home/home-util.h [new file with mode: 0644]
src/home/homectl.c [new file with mode: 0644]
src/home/homed-bus.c [new file with mode: 0644]
src/home/homed-bus.h [new file with mode: 0644]
src/home/homed-home-bus.c [new file with mode: 0644]
src/home/homed-home-bus.h [new file with mode: 0644]
src/home/homed-home.c [new file with mode: 0644]
src/home/homed-home.h [new file with mode: 0644]
src/home/homed-manager-bus.c [new file with mode: 0644]
src/home/homed-manager-bus.h [new file with mode: 0644]
src/home/homed-manager.c [new file with mode: 0644]
src/home/homed-manager.h [new file with mode: 0644]
src/home/homed-operation.c [new file with mode: 0644]
src/home/homed-operation.h [new file with mode: 0644]
src/home/homed-varlink.c [new file with mode: 0644]
src/home/homed-varlink.h [new file with mode: 0644]
src/home/homed.c [new file with mode: 0644]
src/home/homework-cifs.c [new file with mode: 0644]
src/home/homework-cifs.h [new file with mode: 0644]
src/home/homework-directory.c [new file with mode: 0644]
src/home/homework-directory.h [new file with mode: 0644]
src/home/homework-fscrypt.c [new file with mode: 0644]
src/home/homework-fscrypt.h [new file with mode: 0644]
src/home/homework-luks.c [new file with mode: 0644]
src/home/homework-luks.h [new file with mode: 0644]
src/home/homework-mount.c [new file with mode: 0644]
src/home/homework-mount.h [new file with mode: 0644]
src/home/homework-pkcs11.c [new file with mode: 0644]
src/home/homework-pkcs11.h [new file with mode: 0644]
src/home/homework-quota.c [new file with mode: 0644]
src/home/homework-quota.h [new file with mode: 0644]
src/home/homework.c [new file with mode: 0644]
src/home/homework.h [new file with mode: 0644]
src/home/meson.build [new file with mode: 0644]
src/home/org.freedesktop.home1.conf [new file with mode: 0644]
src/home/org.freedesktop.home1.policy [new file with mode: 0644]
src/home/org.freedesktop.home1.service [new file with mode: 0644]
src/home/pam_systemd_home.c [new file with mode: 0644]
src/home/pam_systemd_home.sym [new file with mode: 0644]
src/home/pwquality-util.c [new file with mode: 0644]
src/home/pwquality-util.h [new file with mode: 0644]
src/home/user-record-sign.c [new file with mode: 0644]
src/home/user-record-sign.h [new file with mode: 0644]
src/home/user-record-util.c [new file with mode: 0644]
src/home/user-record-util.h [new file with mode: 0644]
src/hostname/hostnamed.c
src/id128/id128.c
src/import/curl-util.c
src/import/import-raw.c
src/import/importd.c
src/import/pull-raw.c
src/journal-remote/journal-upload.c
src/journal/journal-file.c
src/journal/journal-file.h
src/journal/journal-internal.h
src/journal/journalctl.c
src/journal/journald-context.c
src/journal/journald-kmsg.c
src/journal/journald-native.c
src/journal/journald-native.h
src/journal/journald-rate-limit.c
src/journal/journald-server.c
src/journal/journald-server.h
src/journal/journald-stream.c
src/journal/journald-stream.h
src/journal/journald-syslog.c
src/journal/journald-syslog.h
src/journal/journald.c
src/journal/sd-journal.c
src/kernel-install/00-entry-directory.install
src/kernel-install/50-depmod.install
src/kernel-install/90-loaderentry.install
src/kernel-install/kernel-install
src/libsystemd-network/network-internal.c
src/libsystemd-network/network-internal.h
src/libsystemd-network/sd-dhcp6-client.c
src/libsystemd-network/sd-radv.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-dump.c
src/libsystemd/sd-bus/bus-dump.h
src/libsystemd/sd-bus/bus-introspect.c
src/libsystemd/sd-bus/sd-bus.c
src/libsystemd/sd-bus/test-bus-chat.c
src/libsystemd/sd-bus/test-bus-gvariant.c
src/libsystemd/sd-bus/test-bus-marshal.c
src/libsystemd/sd-bus/test-bus-objects.c
src/libsystemd/sd-bus/test-bus-server.c
src/libsystemd/sd-bus/test-bus-watch-bind.c
src/libsystemd/sd-daemon/sd-daemon.c
src/libsystemd/sd-hwdb/hwdb-util.c
src/libsystemd/sd-id128/id128-util.c
src/libsystemd/sd-id128/id128-util.h
src/libsystemd/sd-id128/sd-id128.c
src/libsystemd/sd-netlink/netlink-message.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-network/network-util.c
src/libsystemd/sd-network/network-util.h
src/locale/keymap-util.c
src/locale/localed.c
src/login/71-seat.rules.in
src/login/loginctl.c
src/login/logind-core.c
src/login/logind-dbus.c
src/login/logind-seat-dbus.c
src/login/logind-seat.c
src/login/logind-session-dbus.c
src/login/logind-session.c
src/login/logind-session.h
src/login/logind-user-dbus.c
src/login/logind-user.c
src/login/logind-user.h
src/login/logind.c
src/login/logind.h
src/login/org.freedesktop.login1.policy
src/login/pam_systemd.c
src/machine/image-dbus.c
src/machine/machine-dbus.c
src/machine/machinectl.c
src/machine/machined-dbus.c
src/machine/machined.c
src/mount/mount-tool.c
src/network/meson.build
src/network/netdev/tunnel.c
src/network/networkctl.c
src/network/networkd-address.c
src/network/networkd-address.h
src/network/networkd-can.c
src/network/networkd-conf.c
src/network/networkd-conf.h
src/network/networkd-dhcp-common.c
src/network/networkd-dhcp-server.c
src/network/networkd-dhcp4.c
src/network/networkd-dhcp4.h
src/network/networkd-gperf.gperf
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-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-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/network/networkd-radv.c
src/network/networkd-radv.h
src/network/networkd-route.c
src/network/networkd-routing-policy-rule.c
src/network/networkd-routing-policy-rule.h
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/codel.h
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-codel.h
src/network/tc/fq.c
src/network/tc/fq.h
src/network/tc/gred.c [new file with mode: 0644]
src/network/tc/gred.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/netem.h
src/network/tc/qdisc.c
src/network/tc/qdisc.h
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/sfq.h
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 [new file with mode: 0644]
src/network/tc/teql.h [new file with mode: 0644]
src/network/test-network.c
src/network/wait-online/link.c
src/network/wait-online/link.h
src/network/wait-online/manager.c
src/network/wait-online/manager.h
src/network/wait-online/wait-online.c
src/nspawn/nspawn-mount.c
src/nspawn/nspawn.c
src/nss-systemd/nss-systemd.c
src/nss-systemd/nss-systemd.sym
src/nss-systemd/userdb-glue.c [new file with mode: 0644]
src/nss-systemd/userdb-glue.h [new file with mode: 0644]
src/partition/makefs.c
src/partition/meson.build [new file with mode: 0644]
src/partition/repart.c [new file with mode: 0644]
src/portable/portable.c
src/portable/portablectl.c
src/portable/portabled-bus.c
src/portable/portabled-image-bus.c
src/portable/portabled.c
src/random-seed/random-seed.c
src/resolve/resolvectl.c
src/resolve/resolved-bus.c
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-trust-anchor.c
src/resolve/resolved-dnssd-bus.c
src/resolve/resolved-dnstls-gnutls.c
src/resolve/resolved-dnstls-openssl.c
src/resolve/resolved-link-bus.c
src/resolve/resolved-manager.c
src/resolve/resolved-mdns.c
src/shared/ask-password-api.c
src/shared/bootspec.c
src/shared/bootspec.h
src/shared/bus-polkit.c [new file with mode: 0644]
src/shared/bus-polkit.h [new file with mode: 0644]
src/shared/bus-unit-util.c
src/shared/bus-util.c
src/shared/bus-util.h
src/shared/conf-parser.c
src/shared/conf-parser.h
src/shared/dissect-image.c
src/shared/dissect-image.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/generator.c
src/shared/generator.h
src/shared/gpt.c [new file with mode: 0644]
src/shared/gpt.h
src/shared/group-record-nss.c [new file with mode: 0644]
src/shared/group-record-nss.h [new file with mode: 0644]
src/shared/group-record-show.c [new file with mode: 0644]
src/shared/group-record-show.h [new file with mode: 0644]
src/shared/group-record.c [new file with mode: 0644]
src/shared/group-record.h [new file with mode: 0644]
src/shared/id128-print.c
src/shared/id128-print.h
src/shared/import-util.c
src/shared/import-util.h
src/shared/install.c
src/shared/journal-util.c
src/shared/journal-util.h
src/shared/json-internal.h
src/shared/json.c
src/shared/libcrypt-util.c [new file with mode: 0644]
src/shared/libcrypt-util.h [new file with mode: 0644]
src/shared/logs-show.c
src/shared/logs-show.h
src/shared/machine-image.c
src/shared/meson.build
src/shared/module-util.c
src/shared/nsflags.c
src/shared/output-mode.h
src/shared/pam-util.c [new file with mode: 0644]
src/shared/pam-util.h [new file with mode: 0644]
src/shared/path-lookup.c
src/shared/path-lookup.h
src/shared/sysctl-util.c
src/shared/sysctl-util.h
src/shared/user-record-nss.c [new file with mode: 0644]
src/shared/user-record-nss.h [new file with mode: 0644]
src/shared/user-record-show.c [new file with mode: 0644]
src/shared/user-record-show.h [new file with mode: 0644]
src/shared/user-record.c [new file with mode: 0644]
src/shared/user-record.h [new file with mode: 0644]
src/shared/userdb.c [new file with mode: 0644]
src/shared/userdb.h [new file with mode: 0644]
src/shared/varlink.c
src/shared/varlink.h
src/sleep/sleep.c
src/socket-proxy/socket-proxyd.c
src/sysctl/sysctl.c
src/systemctl/systemctl.c
src/systemd/_sd-common.h
src/systemd/sd-bus.h
src/systemd/sd-journal.h
src/systemd/sd-netlink.h
src/systemd/sd-radv.h
src/sysusers/sysusers.c
src/test/generate-sym-test.py
src/test/meson.build
src/test/test-ask-password-api.c
src/test/test-btrfs.c
src/test/test-conf-parser.c
src/test/test-dissect-image.c
src/test/test-escape.c
src/test/test-execute.c
src/test/test-format-table.c
src/test/test-fs-util.c
src/test/test-namespace.c
src/test/test-ns.c
src/test/test-path-lookup.c
src/test/test-process-util.c
src/test/test-seccomp.c
src/test/test-sizeof.c
src/test/test-strv.c
src/test/test-sysctl-util.c [new file with mode: 0644]
src/test/test-user-util.c
src/timedate/timedated.c
src/timesync/timesyncd-manager.c
src/tty-ask-password-agent/tty-ask-password-agent.c
src/udev/ata_id/ata_id.c
src/udev/net/link-config-gperf.gperf
src/udev/net/link-config.c
src/udev/net/link-config.h
src/udev/udev-builtin-hwdb.c
src/udev/udev-builtin-input_id.c
src/udev/udev-rules.c
src/udev/udevadm-info.c
src/udev/udevd.c
src/userdb/meson.build [new file with mode: 0644]
src/userdb/userdbctl.c [new file with mode: 0644]
src/userdb/userdbd-manager.c [new file with mode: 0644]
src/userdb/userdbd-manager.h [new file with mode: 0644]
src/userdb/userdbd.c [new file with mode: 0644]
src/userdb/userwork.c [new file with mode: 0644]
src/version/version.h.in
sysctl.d/50-default.conf
test/TEST-01-BASIC/test.sh
test/TEST-02-CRYPTSETUP/test.sh
test/TEST-03-JOBS/test-jobs.sh
test/TEST-03-JOBS/test.sh
test/TEST-04-JOURNAL/test-journal.sh
test/TEST-04-JOURNAL/test.sh
test/TEST-05-RLIMITS/test-rlimits.sh
test/TEST-05-RLIMITS/test.sh
test/TEST-06-SELINUX/test-selinux-checks.sh
test/TEST-06-SELINUX/test.sh
test/TEST-07-ISSUE-1981/test-segfault.sh
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/create-busybox-container
test/TEST-13-NSPAWN-SMOKE/test.sh
test/TEST-14-MACHINE-ID/test.sh
test/TEST-15-DROPIN/test.sh
test/TEST-16-EXTEND-TIMEOUT/assess.sh
test/TEST-16-EXTEND-TIMEOUT/extend_timeout_test_service.sh
test/TEST-16-EXTEND-TIMEOUT/test.sh
test/TEST-17-UDEV-WANTS/test.sh
test/TEST-17-UDEV-WANTS/testsuite.sh
test/TEST-18-FAILUREACTION/test.sh
test/TEST-18-FAILUREACTION/testsuite.sh
test/TEST-19-DELEGATE/test.sh
test/TEST-19-DELEGATE/testsuite.sh
test/TEST-20-MAINPIDGAMES/test.sh
test/TEST-20-MAINPIDGAMES/testsuite.sh
test/TEST-21-SYSUSERS/test-13.expected-group [new file with mode: 0644]
test/TEST-21-SYSUSERS/test-13.expected-passwd [new file with mode: 0644]
test/TEST-21-SYSUSERS/test-13.input [new file with mode: 0644]
test/TEST-21-SYSUSERS/test-14.expected-group [new file with mode: 0644]
test/TEST-21-SYSUSERS/test-14.expected-passwd [new file with mode: 0644]
test/TEST-21-SYSUSERS/test-14.initial-group [new file with mode: 0644]
test/TEST-21-SYSUSERS/test-14.input [new file with mode: 0644]
test/TEST-21-SYSUSERS/test.sh
test/TEST-21-SYSUSERS/unhappy-3.expected-err [new file with mode: 0644]
test/TEST-21-SYSUSERS/unhappy-3.input [new file with mode: 0644]
test/TEST-22-TMPFILES/run-tmpfiles-tests.sh
test/TEST-22-TMPFILES/test-09.sh
test/TEST-22-TMPFILES/test.sh
test/TEST-23-TYPE-EXEC/test.sh
test/TEST-23-TYPE-EXEC/testsuite.sh
test/TEST-24-UNIT-TESTS/test.sh
test/TEST-24-UNIT-TESTS/testsuite.sh
test/TEST-25-IMPORT/test.sh
test/TEST-25-IMPORT/testsuite.sh
test/TEST-26-SETENV/test.sh
test/TEST-26-SETENV/testsuite.sh
test/TEST-27-STDOUTFILE/test.sh
test/TEST-27-STDOUTFILE/testsuite.sh
test/TEST-28-PERCENTJ-WANTEDBY/test.sh
test/TEST-29-UDEV-ID_RENAMING/test.sh
test/TEST-29-UDEV-ID_RENAMING/testsuite.sh
test/TEST-30-ONCLOCKCHANGE/test.sh
test/TEST-30-ONCLOCKCHANGE/testsuite.sh
test/TEST-31-DEVICE-ENUMERATION/test.sh
test/TEST-31-DEVICE-ENUMERATION/testsuite.sh
test/TEST-32-OOMPOLICY/test.sh
test/TEST-32-OOMPOLICY/testsuite.sh
test/TEST-33-CLEAN-UNIT/test.sh
test/TEST-33-CLEAN-UNIT/testsuite.sh
test/TEST-34-DYNAMICUSERMIGRATE/test.sh
test/TEST-34-DYNAMICUSERMIGRATE/testsuite.sh
test/TEST-35-NETWORK-GENERATOR/test.sh
test/TEST-36-NUMAPOLICY/test.sh
test/TEST-36-NUMAPOLICY/testsuite.sh
test/TEST-37-RUNTIMEDIRECTORYPRESERVE/test.sh
test/TEST-37-RUNTIMEDIRECTORYPRESERVE/testsuite.sh
test/TEST-39-EXECRELOAD/test.sh
test/TEST-39-EXECRELOAD/testsuite.sh
test/TEST-40-EXEC-COMMAND-EX/test.sh
test/TEST-40-EXEC-COMMAND-EX/testsuite.sh
test/TEST-41-ONESHOT-RESTART/test.sh
test/TEST-41-ONESHOT-RESTART/testsuite.sh
test/TEST-42-EXECSTOPPOST/test.sh
test/TEST-42-EXECSTOPPOST/testsuite.sh
test/TEST-43-PRIVATEUSER-UNPRIV/test.sh
test/TEST-43-PRIVATEUSER-UNPRIV/testsuite.sh
test/TEST-44-LOG-NAMESPACE/Makefile [new symlink]
test/TEST-44-LOG-NAMESPACE/test.sh [new file with mode: 0755]
test/TEST-44-LOG-NAMESPACE/testsuite.sh [new file with mode: 0755]
test/TEST-45-REPART/Makefile [new symlink]
test/TEST-45-REPART/test.sh [new file with mode: 0755]
test/TEST-45-REPART/testsuite.sh [new file with mode: 0755]
test/TEST-46-HOMED/Makefile [new symlink]
test/TEST-46-HOMED/test.sh [new file with mode: 0755]
test/TEST-46-HOMED/testsuite.sh [new file with mode: 0755]
test/TEST-47-ISSUE-14566/Makefile [new symlink]
test/TEST-47-ISSUE-14566/repro.sh [new file with mode: 0755]
test/TEST-47-ISSUE-14566/test.sh [new file with mode: 0755]
test/TEST-47-ISSUE-14566/testsuite.sh [new file with mode: 0755]
test/fuzz/fuzz-link-parser/directives.link
test/fuzz/fuzz-network-parser/directives.network
test/fuzz/fuzz-network-parser/oss-fuzz-20548 [new file with mode: 0644]
test/fuzz/fuzz-unit-file/directives.service
test/meson.build
test/mkosi.default.networkd-test
test/mocks/fsck
test/run-integration-tests.sh
test/test-efi-create-disk.sh
test/test-exec-deserialization.py
test/test-execute/exec-basic.service
test/test-execute/exec-capabilityboundingset-invert.service
test/test-execute/exec-privatedevices-no-capability-mknod.service
test/test-execute/exec-privatedevices-no-capability-sys-rawio.service
test/test-execute/exec-privatedevices-yes-capability-mknod.service
test/test-execute/exec-privatedevices-yes-capability-sys-rawio.service
test/test-execute/exec-privatetmp-disabled-by-prefix.service [new file with mode: 0644]
test/test-execute/exec-protectkernellogs-no-capabilities.service
test/test-execute/exec-protectkernellogs-yes-capabilities.service
test/test-execute/exec-protectkernelmodules-no-capabilities.service
test/test-execute/exec-protectkernelmodules-yes-capabilities.service
test/test-execute/exec-standardinput-file-cat.service [new file with mode: 0644]
test/test-functions
test/test-network/conf/25-fibrule-uidrange.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-fq-codel.network [deleted file]
test/test-network/conf/25-qdisc-ingress-netem-compat.network [new file with mode: 0644]
test/test-network/conf/25-qdisc-netem-and-fqcodel.network [deleted file]
test/test-network/conf/25-qdisc-tbf-and-sfq.network [deleted file]
test/test-network/conf/25-route-vrf.network [new file with mode: 0644]
test/test-network/conf/dhcp-client-gateway-ipv4.network
test/test-network/conf/dhcp-client-gateway-ipv6.network
test/test-network/conf/dhcp-client-ipv4-use-routes-no.network [new file with mode: 0644]
test/test-network/conf/ipv6-prefix-veth-token-prefixstable.network [new file with mode: 0644]
test/test-network/conf/ipv6-prefix-veth-token-static-explicit.network [new file with mode: 0644]
test/test-network/conf/ipv6-prefix-veth-token-static-multiple.network [new file with mode: 0644]
test/test-network/conf/ipv6-prefix-veth-token-static.network [new file with mode: 0644]
test/test-network/conf/ipv6-prefix.network
test/test-network/conf/ipv6ra-prefix.network
test/test-network/systemd-networkd-tests.py
tmpfiles.d/systemd.conf.m4
tools/check-directives.sh
tools/chromiumos/gen_autosuspend_rules.py
tools/coverity.sh
tools/meson-vcs-tag.sh
tools/oss-fuzz.sh
travis-ci/managers/debian.sh
travis-ci/managers/fedora.sh
travis-ci/managers/fuzzbuzz.sh
travis-ci/managers/fuzzit.sh
units/blockdev@.target [new file with mode: 0644]
units/initrd-cleanup.service [moved from units/initrd-cleanup.service.in with 88% similarity]
units/initrd-fs.target
units/initrd-parse-etc.service [moved from units/initrd-parse-etc.service.in with 77% similarity]
units/initrd-root-device.target
units/initrd-root-fs.target
units/initrd-switch-root.service [moved from units/initrd-switch-root.service.in with 89% similarity]
units/initrd-udevadm-cleanup-db.service [moved from units/initrd-udevadm-cleanup-db.service.in with 93% similarity]
units/initrd.target
units/local-fs.target
units/meson.build
units/modprobe@.service
units/sys-kernel-tracing.mount [new file with mode: 0644]
units/systemd-ask-password-console.service [moved from units/systemd-ask-password-console.service.in with 90% similarity]
units/systemd-ask-password-wall.service [moved from units/systemd-ask-password-wall.service.in with 68% similarity]
units/systemd-boot-system-token.service [moved from units/systemd-boot-system-token.service.in with 96% similarity]
units/systemd-firstboot.service [moved from units/systemd-firstboot.service.in with 87% similarity]
units/systemd-halt.service [moved from units/systemd-halt.service.in with 93% similarity]
units/systemd-homed.service.in [new file with mode: 0644]
units/systemd-hwdb-update.service.in
units/systemd-journal-catalog-update.service [moved from units/systemd-journal-catalog-update.service.in with 93% similarity]
units/systemd-journal-flush.service [moved from units/systemd-journal-flush.service.in with 87% similarity]
units/systemd-journald-varlink@.socket [new file with mode: 0644]
units/systemd-journald.service.in
units/systemd-journald@.service.in [new file with mode: 0644]
units/systemd-journald@.socket [new file with mode: 0644]
units/systemd-kexec.service [moved from units/systemd-kexec.service.in with 93% similarity]
units/systemd-machine-id-commit.service [moved from units/systemd-machine-id-commit.service.in with 92% similarity]
units/systemd-machined.service.in
units/systemd-network-generator.service.in
units/systemd-networkd.service.in
units/systemd-nspawn@.service.in
units/systemd-pstore.service.in
units/systemd-repart.service.in [new file with mode: 0644]
units/systemd-sysusers.service [moved from units/systemd-sysusers.service.in with 94% similarity]
units/systemd-tmpfiles-clean.service [moved from units/systemd-tmpfiles-clean.service.in with 92% similarity]
units/systemd-tmpfiles-setup-dev.service [moved from units/systemd-tmpfiles-setup-dev.service.in with 90% similarity]
units/systemd-tmpfiles-setup.service [moved from units/systemd-tmpfiles-setup.service.in with 89% similarity]
units/systemd-udev-settle.service [moved from units/systemd-udev-settle.service.in with 95% similarity]
units/systemd-udev-trigger.service [moved from units/systemd-udev-trigger.service.in with 82% similarity]
units/systemd-udevd.service.in
units/systemd-userdbd.service.in [new file with mode: 0644]
units/systemd-userdbd.socket [new file with mode: 0644]
units/systemd-vconsole-setup.service.in
units/user/meson.build
units/user/systemd-tmpfiles-clean.service [moved from units/user/systemd-tmpfiles-clean.service.in with 91% similarity]
units/user/systemd-tmpfiles-setup.service [moved from units/user/systemd-tmpfiles-setup.service.in with 90% similarity]
units/user@.service.in

index ee3b802243a4a549cc38ea43db37311429176582..b720b58a20032bb5175aa5d2f5516b1a114fb632 100644 (file)
@@ -1 +1 @@
-custom: ['https://spi-inc.org/projects/systemd/', 'https://www.paypal.com/donate/?token=fBGzXDOyIGobZH3oEhYQlYlA61OMRXVnF9XXQqNNehRs-nliAU5XxozIh9z-hlmE-xXC-m']
+custom: ['https://spi-inc.org/projects/systemd/']
index 5948d8c2bc0d7fc7f941cd62c843f4bd6b7ed887..79512df980568acc59bd0b61ae390ecd9074d0b0 100644 (file)
--- a/.lgtm.yml
+++ b/.lgtm.yml
@@ -1,13 +1,14 @@
+---
+# vi: ts=2 sw=2 et:
+
 extraction:
   cpp:
     prepare:
       packages:
-        - python3-pip
-        - python3-setuptools
-        - python3-wheel
-    after_prepare:
-      - pip3 install meson
-      - export PATH="$HOME/.local/bin/:$PATH"
+        - libpwquality-dev
+        - libfdisk-dev
+        - libp11-kit-dev
+        - libssl-dev
   python:
     python_setup:
       version: 3
index 2bfc64695ecb644c564cae4df2d9eb9e252d0c73..3f3af64d777c4259fb20ec21a663808b58d90bf5 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -30,6 +30,7 @@ Daniel Machon <Danielmachon@live.dk>
 Daniel Rusek <mail@asciiwolf.com>
 Daniel Stekloff <dsteklof@us.ibm.com>
 Daniel Șerbănescu <dasj19@users.noreply.github.com>
+Dann Frazier <dann.frazier@canonical.com>
 Dave Reisner <dreisner@archlinux.org> <d@falconindy.com>
 David Zeuthen <david@fubar.dk>
 David Zeuthen <david@fubar.dk> <davidz@redhat.com>
@@ -164,6 +165,7 @@ Stefan Schweter <stefan@schweter.it>
 Stuart McLaren <stuart.mclaren@hp.com>
 Susant Sahani <ssahani@gmail.com> <145210+ssahani@users.noreply.github.com>
 Susant Sahani <ssahani@gmail.com> <susant@redhat.com>
+Sylvain Plantefeve <sylvain.plantefeve@gmail.com>
 Sébastien Bacher <seb128@ubuntu.com>
 Tanu Kaskinen <TanuKaskinen@web>
 Ted Ts'o <tytso@mit.edu>
index 911908cb77161c55dba50f969aa19126eb70b26c..01bfd2338bbcfb5ad1350786dd6df881bbe5b588 100644 (file)
@@ -8,9 +8,8 @@ Distribution=fedora
 Release=31
 
 [Output]
-Format=raw_btrfs
+Format=gpt_ext4
 Bootable=yes
-KernelCommandLine=printk.devkmsg=on
 
 [Partitions]
 RootSize=3G
@@ -27,6 +26,7 @@ BuildPackages=
         gcc
         gettext
         git
+        glibc-minimal-langpack
         gnu-efi
         gnu-efi-devel
         gnutls-devel
@@ -38,19 +38,22 @@ BuildPackages=
         libblkid-devel
         libcap-devel
         libcurl-devel
+        libfdisk-devel
         libgcrypt-devel
         libidn2-devel
         libmicrohttpd-devel
         libmount-devel
+        libpwquality-devel
         libseccomp-devel
         libselinux-devel
-        libtool
         libxkbcommon-devel
         libxslt
         lz4
         lz4-devel
         m4
         meson
+        openssl-devel
+        p11-kit-devel
         pam-devel
         pcre2-devel
         pkgconfig
@@ -58,10 +61,18 @@ BuildPackages=
         python3-lxml
         qrencode-devel
         tree
+        valgrind-devel
         xz-devel
 
 Packages=
+        coreutils
+        cryptsetup-libs
+        kmod-libs
+        e2fsprogs
         libidn2
+        libseccomp
+        procps-ng
+        util-linux
 
 BuildDirectory=mkosi.builddir
 Cache=mkosi.cache
diff --git a/NEWS b/NEWS
index 4fe5799c0cde7aa25fec8ca5d4db174352630a8d..7f241fd6429aaf79cd81bff62f5caba366fbdf8b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,314 @@
 systemd System and Service Manager
 
+CHANGES WITH 245:
+
+        * A new tool "systemd-repart" has been added, that operates as an
+          idempotent declarative repartitioner for GPT partition tables.
+          Specifically, a set of partitions that must or may exist can be
+          configured via drop-in files, and during every boot the partition
+          table on disk is compared with these files, creating missing
+          partitions or growing existing ones based on configurable relative
+          and absolute size constraints. The tool is strictly incremental,
+          i.e. does not delete, shrink or move partitions, but only adds and
+          grows them. The primary use-case is OS images that ship in minimized
+          form, that on first boot are grown to the size of the underlying
+          block device or augmented with additional partitions. For example,
+          the root partition could be extended to cover the whole disk, or a
+          swap or /home partitions could be added on first boot. It can also be
+          used for systems that use an A/B update scheme but ship images with
+          just the A partition, with B added on first boot. The tool is
+          primarily intended to be run in the initrd, shortly before
+          transitioning into the host OS, but can also be run after the
+          transition took place. It automatically discovers the disk backing
+          the root file system, and should hence not require any additional
+          configuration besides the partition definition drop-ins. If no
+          configuration drop-ins are present, no action is taken.
+
+        * A new component "userdb" has been added, along with a small daemon
+          "systemd-userdb.service" and a client tool "userdbctl". The framework
+          allows defining rich user and group records in a JSON format,
+          extending on the classic "struct passwd" and "struct group"
+          structures. Various components in systemd have been updated to
+          process records in this format, including systemd-logind and
+          pam-systemd. The user records are intended to be extensible, and
+          allow setting various resource management, security and runtime
+          parameters that shall be applied to processes and sessions of the
+          user as they log in. This facility is intended to allow associating
+          such metadata directly with user/group records so that they can be
+          produced, extended and consumed in unified form. We hope that
+          eventually frameworks such as sssd will generate records this way, so
+          that for the first time resource management and various other
+          per-user settings can be configured in LDAP directories and then
+          provided to systemd (specifically to systemd-logind and pam-system)
+          to apply on login. For further details see:
+
+          https://systemd.io/USER_RECORD
+          https://systemd.io/GROUP_RECORD
+          https://systemd.io/USER_GROUP_API
+
+        * A small new service systemd-homed.service has been added, that may be
+          used to securely manage home directories with built-in encryption.
+          The complete user record data is unified with the home directory,
+          thus making home directories naturally migratable. Its primary
+          back-end is based on LUKS volumes, but fscrypt, plain directories,
+          and other storage schemes are also supported. This solves a couple of
+          problems we saw with traditional ways to manage home directories, in
+          particular when it comes to encryption. For further discussion of
+          this, see the video of Lennart's talk at AllSystemsGo! 2019:
+
+          https://media.ccc.de/v/ASG2019-164-reinventing-home-directories
+
+          For further details about the format and expectations on home
+          directories this new daemon makes, see:
+
+          https://systemd.io/HOME_DIRECTORY
+
+        * systemd-journald is now multi-instantiable. In addition to the main
+          instance systemd-journald.service there's now a template unit
+          systemd-journald@.service, with each instance defining a new named
+          log 'namespace' (whose name is specified via the instance part of the
+          unit name). A new unit file setting LogNamespace= has been added,
+          taking such a namespace name, that assigns services to the specified
+          log namespaces. As each log namespace is serviced by its own
+          independent journal daemon, this functionality may be used to improve
+          performance and increase isolation of applications, at the price of
+          losing global message ordering. Each instance of journald has a
+          separate set of configuration files, with possibly different disk
+          usage limitations and other settings.
+
+          journalctl now takes a new option --namespace= to show logs from a
+          specific log namespace. The sd-journal.h API gained
+          sd_journal_open_namespace() for opening the log stream of a specific
+          log namespace. systemd-journald also gained the ability to exit on
+          idle, which is useful in the context of log namespaces, as this means
+          log daemons for log namespaces can be activated automatically on
+          demand and will stop automatically when no longer used, minimizing
+          resource usage.
+
+        * When systemd-tmpfiles copies a file tree using the 'C' line type it
+          will now label every copied file according to the SELinux database.
+
+        * When systemd/PID 1 detects it is used in the initrd it will now boot
+          into initrd.target rather than default.target by default. This should
+          make it simpler to build initrds with systemd as for many cases the
+          only difference between a host OS image and an initrd image now is
+          the presence of the /etc/initrd-release file.
+
+        * A new kernel command line option systemd.cpu_affinity= is now
+          understood. It's equivalent to the CPUAffinity= option in
+          /etc/systemd/system.conf and allows setting the CPU mask for PID 1
+          itself and the default for all other processes.
+
+        * When systemd/PID 1 is reloaded (with systemctl daemon-reload or
+          equivalent), the SELinux database is now reloaded, ensuring that
+          sockets and other file system objects are generated taking the new
+          database into account.
+
+        * systemd/PID 1 accepts a new "systemd.show-status=error" setting, and
+          "quiet" has been changed to imply that instead of
+          "systemd.show-status=auto". In this mode, only messages about errors
+          and significant delays in boot are shown on the console.
+
+        * The sd-event.h API gained native support for the new Linux "pidfd"
+          concept. This permits watching processes using file descriptors
+          instead of PID numbers, which fixes a number of races and makes
+          process supervision more robust and efficient. All of systemd's
+          components will now use pidfds if the kernel supports it for process
+          watching, with the exception of PID 1 itself, unfortunately. We hope
+          to move PID 1 to exclusively using pidfds too eventually, but this
+          requires some more kernel work first. (Background: PID 1 watches
+          processes using waitid() with the P_ALL flag, and that does not play
+          together nicely with pidfds yet.)
+
+        * Closely related to this, the sd-event.h API gained two new calls
+          sd_event_source_send_child_signal() (for sending a signal to a
+          watched process) and sd_event_source_get_child_process_own() (for
+          marking a process so that it is killed automatically whenever the
+          event source watching it is freed).
+
+        * systemd-networkd gained support for configuring Token Bucket Filter
+          (TBF) parameters in its qdisc configuration support. Similarly,
+          support for Stochastic Fairness Queuing (SFQ), Controlled-Delay
+          Active Queue Management (CoDel), and Fair Queue (FQ) has been added.
+
+        * systemd-networkd gained support for Intermediate Functional Block
+          (IFB) network devices.
+
+        * systemd-networkd gained support for configuring multi-path IP routes,
+          using the new MultiPathRoute= setting in the [Route] section.
+
+        * systemd-networkd's DHCPv4 client has been updated to support a new
+          SendDecline= option. If enabled, duplicate address detection is done
+          after a DHCP offer is received from the server. If a conflict is
+          detected, the address is declined. The DHCPv4 client also gained
+          support for a new RouteMTUBytes= setting that allows to configure the
+          MTU size to be used for routes generated from DHCPv4 leases.
+
+        * The PrefixRoute= setting in systemd-networkd's [Address] section of
+          .network files has been deprecated, and replaced by AddPrefixRoute=,
+          with its sense inverted.
+
+        * The Gateway= setting of [Route] sections of .network files gained
+          support for a special new value "_dhcp". If set, the configured
+          static route uses the gateway host configured via DHCP.
+
+        * New User= and SuppressPrefixLength= settings have been implemented
+          for the [RoutingPolicyRule] section of .network files to configure
+          source routing based on UID ranges and prefix length, respectively.
+
+        * sd-bus gained a new API call sd_bus_message_sensitive() that marks a
+          D-Bus message object as "sensitive". Those objects are erased from
+          memory when they are freed. This concept is intended to be used for
+          messages that contain security sensitive data. A new flag
+          SD_BUS_VTABLE_SENSITIVE has been introduced as well to mark methods
+          in sd-bus vtables, causing any incoming and outgoing messages of
+          those methods to be implicitly marked as "sensitive".
+
+        * sd-bus gained a new API call sd_bus_message_dump() for dumping the
+          contents of a message (or parts thereof) to standard output for
+          debugging purposes.
+
+        * 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.
+
+        * The support for /etc/crypttab gained a new x-initrd.attach option. If
+          set, the specified encrypted volume is unlocked already in the
+          initrd. This concept corresponds to the x-initrd.mount option in
+          /etc/fstab.
+
+        * systemd-cryptsetup gained native support for unlocking encrypted
+          volumes utilizing PKCS#11 smartcards, i.e. for example to bind
+          encryption of volumes to YubiKeys. This is exposed in the new
+          pkcs11-uri= option in /etc/crypttab.
+
+        * The /etc/fstab support in systemd now supports two new mount options
+          x-systemd.{required,wanted}-by=, for explicitly configuring the units
+          that the specified mount shall be pulled in by, in place of
+          the usual local-fs.target/remote-fs.target.
+
+        * The https://systemd.io/ web site has been relaunched, directly
+          populated with most of the documentation included in the systemd
+          repository. systemd also acquired a new logo, thanks to Tobias
+          Bernard.
+
+        * systemd-udevd gained support for managing "alternative" network
+          interface names, as supported by new Linux kernels. For the first
+          time this permits assigning multiple (and longer!) names to a network
+          interface. systemd-udevd will now by default assign the names
+          generated via all supported naming schemes to each interface. This
+          may be further tweaked with .link files and the AlternativeName= and
+          AlternativeNamesPolicy= settings. Other components of systemd have
+          been updated to support the new alternative names wherever
+          appropriate. For example, systemd-nspawn will now generate
+          alternative interface names for the host-facing side of container
+          veth links based on the full container name without truncation.
+
+        * systemd-nspawn interface naming logic has been updated in another way
+          too: if the main interface name (i.e. as opposed to new-style
+          "alternative" names) based on the container name is truncated, a
+          simple hashing scheme is used to give different interface names to
+          multiple containers whose names all begin with the same prefix. Since
+          this changes the primary interface names pointing to containers if
+          truncation happens, the old scheme may still be requested by
+          selecting an older naming scheme, via the net.naming-scheme= kernel
+          command line option.
+
+        * PrivateUsers= in service files now works in services run by the
+          systemd --user per-user instance of the service manager.
+
+        * A new per-service sandboxing option ProtectClock= has been added that
+          locks down write access to the system clock. It takes away device
+          node access to /dev/rtc as well as the system calls that set the
+          system clock and the CAP_SYS_TIME and CAP_WAKE_ALARM capabilities.
+          Note that this option does not affect access to auxiliary services
+          that allow changing the clock, for example access to
+          systemd-timedated.
+
+        * The systemd-id128 tool gained a new "show" verb for listing or
+          resolving a number of well-known UUIDs/128bit IDs, currently mostly
+          GPT partition table types.
+
+        * The Discoverable Partitions Specification has been updated to support
+          /var and /var/tmp partition discovery. Support for this has been
+          added to systemd-gpt-auto-generator. For details see:
+
+          https://systemd.io/DISCOVERABLE_PARTITIONS
+
+        * "systemctl list-unit-files" has been updated to show a new column
+          with the suggested enablement state based on the vendor preset files
+          for the respective units.
+
+        * "systemctl" gained a new option "--with-dependencies". If specified
+          commands such as "systemctl status" or "systemctl cat" will now show
+          all specified units along with all units they depend on.
+
+        * networkctl gained support for showing per-interface logs in its
+          "status" output.
+
+        * systemd-networkd-wait-online gained support for specifying the maximum
+          operational state to wait for, and to wait for interfaces to
+          disappear.
+
+        * The [Match] section of .link and .network files now supports a new
+          option PermanentMACAddress= which may be used to check against the
+          permanent MAC address of a network device even if a randomized MAC
+          address is used.
+
+        * The [TrafficControlQueueingDiscipline] section in .network files has
+          been renamed to [NetworkEmulator] with the "NetworkEmulator" prefix
+          dropped from the individual setting names.
+
+        * Any .link and .network files that have an empty [Match] section (this
+          also includes empty and commented-out files) will now be
+          rejected. systemd-udev and systemd-networkd started warning about
+          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
+          with at least one session on a local VT are granted permission.
+
+        * When systemd sets up PAM sessions that invoked service processes
+          shall run in, the pam_setcred() API is now invoked, thus permitting
+          PAM modules to set additional credentials for the processes.
+
+        * portablectl attach/detach verbs now accept --now and --enable options
+          to combine attachment with enablement and invocation, or detachment
+          with stopping and disablement.
+
+        Contributions from: AJ Bagwell, Alin Popa, Andreas Rammhold, Anita
+        Zhang, Ansgar Burchardt, Antonio Russo, Arian van Putten, Ashley Davis,
+        Balint Reczey, Bart Willems, Bastien Nocera, Benjamin Dahlhoff, Charles
+        (Chas) Williams, cheese1, Chris Down, Chris Murphy, Christian Ehrhardt,
+        Christian Göttsche, cvoinf, Daan De Meyer, Daniele Medri, Daniel Rusek,
+        Daniel Shahaf, Dann Frazier, Dan Streetman, Dariusz Gadomski, David
+        Michael, Dimitri John Ledkov, Emmanuel Bourg, Evgeny Vereshchagin,
+        ezst036, Felipe Sateler, Filipe Brandenburger, Florian Klink, Franck
+        Bui, Fran Dieguez, Frantisek Sumsal, Greg "GothAck" Miell, Guilhem
+        Lettron, Guillaume Douézan-Grard, Hans de Goede, HATAYAMA Daisuke, Iain
+        Lane, James Buren, Jan Alexander Steffens (heftig), Jérémy Rosen, Jin
+        Park, Jun'ichi Nomura, Kai Krakow, Kevin Kuehler, Kevin P. Fleming,
+        Lennart Poettering, Leonid Bloch, Leonid Evdokimov, lothrond, Luca
+        Boccassi, Lukas K, Lynn Kirby, Mario Limonciello, Mark Deneen, Matthew
+        Leeds, Michael Biebl, Michal Koutný, Michal Sekletár, Mike Auty, Mike
+        Gilbert, mtron, nabijaczleweli, Naïm Favier, Nate Jones, Norbert Lange,
+        Oliver Giles, Paul Davey, Paul Menzel, Peter Hutterer, Piotr Drąg, Rafa
+        Couto, Raphael, rhn, Robert Scheck, Rocka, Romain Naour, Ryan Attard,
+        Sascha Dewald, Shengjing Zhu, Slava Kardakov, Spencer Michaels, Sylvain
+        Plantefeve, Stanislav Angelovič, Susant Sahani, Thomas Haller, Thomas
+        Schmitt, Timo Schlüßler, Timo Wilken, Tobias Bernard, Tobias Klauser,
+        Tobias Stoeckmann, Topi Miettinen, tsia, WataruMatsuoka, Wieland
+        Hoffmann, Wilhelm Schuster, Will Fleming, xduugu, Yong Cong Sin, Yuri
+        Chornoivan, Yu Watanabe, Zach Smith, Zbigniew Jędrzejewski-Szmek, Zeyu
+        DONG
+
+        – Warsaw, 2020-03-06
+
 CHANGES WITH 244:
 
         * Support for the cpuset cgroups v2 controller has been added.
@@ -675,32 +984,33 @@ CHANGES WITH 243:
         Contributions from: Aaron Barany, Adrian Bunk, Alan Jenkins, Albrecht
         Lohofener, Andrej Valek, Anita Zhang, Arian van Putten, Balint Reczey,
         Bastien Nocera, Ben Boeckel, Benjamin Robin, camoz, Chen Qi, Chris
-        Chiu, Chris Down, Christian Kellner, Clinton Roy, Connor Reeder, Daniel
-        Black, Daniele Medri, Dan Streetman, Dave Reisner, Dave Ross, David
-        Art, David Tardon, Debarshi Ray, Dimitri John Ledkov, Dominick Grift,
-        Donald Buczek, Douglas Christman, Eric DeVolder, EtherGraf, Evgeny
-        Vereshchagin, Feldwor, Felix Riemann, Florian Dollinger, Francesco
-        Pennica, Franck Bui, Frantisek Sumsal, Franz Pletz, frederik, Hans
-        de Goede, Iago López Galeiras, Insun Pyo, Ivan Shapovalov, Iwan Timmer,
-        Jack, Jakob Unterwurzacher, Jan Chren, Jan Klötzke, Jan Losinski, Jan
-        Pokorný, Jan Synacek, Jan-Michael Brummer, Jeka Pats, Jeremy Soller,
-        Jérémy Rosen, Jiri Pirko, Joe Lin, Joerg Behrmann, Joe Richey, Jóhann
-        B. Guðmundsson, Johannes Christ, Johannes Schmitz, Jonathan Rouleau,
-        Jorge Niedbalski, Kai Krakow, Kai Lüke, Karel Zak, Kashyap Chamarthy,
+        Chiu, Chris Down, Christian Göttsche, Christian Kellner, Clinton Roy,
+        Connor Reeder, Daniel Black, Daniel Lublin, Daniele Medri, Dan
+        Streetman, Dave Reisner, Dave Ross, David Art, David Tardon, Debarshi
+        Ray, Dimitri John Ledkov, Dominick Grift, Donald Buczek, Douglas
+        Christman, Eric DeVolder, EtherGraf, Evgeny Vereshchagin, Feldwor,
+        Felix Riemann, Florian Dollinger, Francesco Pennica, Franck Bui,
+        Frantisek Sumsal, Franz Pletz, frederik, Hans de Goede, Iago López
+        Galeiras, Insun Pyo, Ivan Shapovalov, Iwan Timmer, Jack, Jakob
+        Unterwurzacher, Jan Chren, Jan Klötzke, Jan Losinski, Jan Pokorný, Jan
+        Synacek, Jan-Michael Brummer, Jeka Pats, Jeremy Soller, Jérémy Rosen,
+        Jiri Pirko, Joe Lin, Joerg Behrmann, Joe Richey, Jóhann B. Guðmundsson,
+        Johannes Christ, Johannes Schmitz, Jonathan Rouleau, Jorge Niedbalski,
+        Jörg Thalheim, Kai Krakow, Kai Lüke, Karel Zak, Kashyap Chamarthy,
         Krayushkin Konstantin, Lennart Poettering, Lubomir Rintel, Luca
         Boccassi, Luís Ferreira, Marc-André Lureau, Markus Felten, Martin Pitt,
         Matthew Leeds, Mattias Jernberg, Michael Biebl, Michael Olbrich,
         Michael Prokop, Michael Stapelberg, Michael Zhivich, Michal Koutný,
         Michal Sekletar, Mike Gilbert, Milan Broz, Miroslav Lichvar, mpe85,
         Mr-Foo, Network Silence, Oliver Harley, pan93412, Paul Menzel, pEJipE,
-        Peter A. Bigot, Philip Withnall, Piotr Drąg, Rafael Fontenelle, Roberto
-        Santalla, Ronan Pigott, root, RussianNeuroMancer, Sebastian Jennen,
-        shinygold, Shreyas Behera, Simon Schricker, Susant Sahani, Thadeu Lima
-        de Souza Cascardo, Theo Ouzhinski, Thiebaud Weksteen, Thomas Haller,
-        Thomas Weißschuh, Tomas Mraz, Tommi Rantala, Topi Miettinen, VD-Lycos,
-        ven, Wieland Hoffmann, William A. Kennington III, William Wold, Xi
-        Ruoyao, Yuri Chornoivan, Yu Watanabe, Zach Smith, Zbigniew
-        Jędrzejewski-Szmek, Zhang Xianwei
+        Peter A. Bigot, Philip Withnall, Piotr Drąg, Rafael Fontenelle, Robert
+        Scheck, Roberto Santalla, Ronan Pigott, root, RussianNeuroMancer,
+        Sebastian Jennen, shinygold, Shreyas Behera, Simon Schricker, Susant
+        Sahani, Thadeu Lima de Souza Cascardo, Theo Ouzhinski, Thiebaud
+        Weksteen, Thomas Haller, Thomas Weißschuh, Tomas Mraz, Tommi Rantala,
+        Topi Miettinen, VD-Lycos, ven, Vladimir Yerilov, Wieland Hoffmann,
+        William A. Kennington III, William Wold, Xi Ruoyao, Yuri Chornoivan,
+        Yu Watanabe, Zach Smith, Zbigniew Jędrzejewski-Szmek, Zhang Xianwei
 
         – Camerino, 2019-09-03
 
@@ -6914,10 +7224,9 @@ CHANGES WITH 213:
         * A new fsck.repair= kernel option has been added to control
           how fsck shall deal with unclean file systems at boot.
 
-        * The (.ini) configuration file parser will now silently
-          ignore sections whose name begins with "X-". This may be
-          used to maintain application-specific extension sections in unit
-          files.
+        * The (.ini) configuration file parser will now silently ignore
+          sections whose names begin with "X-". This may be used to maintain
+          application-specific extension sections in unit files.
 
         * machined gained a new API to query the IP addresses of
           registered containers. "machinectl status" has been updated
diff --git a/TODO b/TODO
index 961767187bfde4f30ae218fc4a7b07181df235d1..e944245a57b4aa69a1c2ff58053e136e08c441e1 100644 (file)
--- a/TODO
+++ b/TODO
@@ -19,12 +19,109 @@ Janitorial Clean-ups:
 
 Features:
 
+* cryptsetup/homed: also support FIDO2 HMAC password logic for unlocking
+  devices. (see: https://github.com/mjec/fido2-hmac-secret)
+
+* 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
+  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.
+
+* bootctl:
+  - teach it to prepare an ESP wholesale, i.e. with mkfs.vfat invocation
+  - 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
+
+* 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.
+
+* cryptsetup: reimplement the mkswap/mke2fs in cryptsetup-generator to use
+  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
 
+* unify on openssl:
+  - port sd_id128_get_machine_app_specific() over from khash
+  - port resolved over from libgcrypt (DNSSEC code)
+  - port journald + fsprg over from libgcrypt
+  - port importd over from libgcrypt
+  - when that's done: kill khash.c
+  - when that's done: kill gnutls support in resolved
+
 * kill zenata, all hail weblate?
 
-* move discoverable partitions spec into markdown and our tree
+* when we resize disks (homed?) always round up to 4K sectors, not 512K
+
+* add growvol and makevol options for /etc/crypttab, similar to
+  x-systemd.growfs and x-systemd-makefs.
+
+* hook up the TPM to /etc/crypttab, with a new option that is similar to the
+  new PKCS#11 option in crypttab, and allows unlocking a LUKS volume via a key
+  unsealed from the TPM. Optionally, if TPM is not available fall back to
+  TPM-less mode, and set up linear DM mapping instead (inspired by kpartx), so
+  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
+  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
+  for the two partition types explicitly. And provide an internal mapping so
+  that "Type=linux-generic" maps to the right types for both partition tables
+  automatically.
+
+* systemd-repart: allow sizing partitions as factor of available RAM, so that
+  we can reasonably size swap partitions for hibernation.
+
+* systemd-repart: allow running mkfs before making partitions pop up +
+  encryption via LUKS to allow booting into an empty root with only /usr mounted in
+
+* systemd-repart: allow managing the gpt read-only partition flag + auto-mount flag
+
+* 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
+
+* systemd-repart: allow config of partition uuid
+
+* userdb: allow username prefix searches in varlink API
+
+* userdb: allow existence checks
+
+* pid: 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
+  initrd had set.
 
 * sd-event: add native support for P_ALL waitid() watching, then move PID 1 to
   it fo reaping assigned but unknown children. This needs to some special care
@@ -44,8 +141,6 @@ Features:
   shouldn't operate in a volatile mode unless we got told so from a trusted
   source.
 
-* look for /var/tmp automatically via gpt auto discovery
-
 * figure out automatic partition discovery when combining writable root dir
   with immutable /usr
 
@@ -78,10 +173,6 @@ Features:
   right) become genuine first class citizens, and we gain automatic, sane JSON
   output for them.
 
-* dissector: invoke fsck on the file systems we encounter, after all ext4 is
-  still pretty popular (and we mount the ESP too with it after all, which is
-  fat)
-
 * 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.
 
@@ -104,6 +195,38 @@ Features:
   user@.service, which returns the XDG_RUNTIME_DIR value, and make this
   behaviour selectable via pam module option.
 
+* 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?
+  - properties: icon url?, preferred session type?, administrator bool (which translates to 'wheel' membership)?, address?, telephone?, vcard?, samba stuff?, parental controls?
+  - communicate clearly when usb stick is safe to remove. probably involves
+    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)
+  - 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
+  - description field for groups
+  - make size of $XDG_RUNTIME_DIR configurable in user record
+  - reuse pwquality magic in firstboot
+  - query password from kernel keyring first
+  - update even if record is "absent"
+  - add a "access mode" + "fstype" field to the "status" section of json identity records reflecting the actually used access mode and fstype, even on non-luks backends
+  - move acct mgmt stuff from pam_systemd_home to pam_systemd?
+  - when "homectl --pkcs11-token-uri=" is used, synthesize ssh-authorized-keys records for all keys we have private keys on the stick for
+  - 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 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
   combination of AT_RANDOM + getpid() + the starttime from
@@ -174,13 +297,6 @@ Features:
 
 * introduce per-unit (i.e. per-slice, per-service) journal log size limits.
 
-* optionally, if a per-partition GPT flag is set for the root/home/… partitions
-  format the partition on next boot and unset the flag, in order to implement
-  factory reset. also, add a second flag that simply indicates whether such a
-  scheme is supported. then, add a tool (or maybe beef up systemd-dissect) to
-  show state of these flags, and optionally trigger such a factory reset on
-  next boot by setting the flag.
-
 * sd-boot: automatically load EFI modules from some drop-in dir, so that people
   can add in file system drivers and such
 
@@ -212,7 +328,7 @@ Features:
 
 * the a-posteriori stopping of units bound to units that disappeared logic
   should be reworked: there should be a queue of units, and we should only
-  enqeue stop jobs from a defer event that processes queue instead of
+  enqueue stop jobs from a defer event that processes queue instead of
   right-away when we find a unit that is bound to one that doesn't exist
   anymore. (similar to how the stop-unneeded queue has been reworked the same
   way)
@@ -394,10 +510,6 @@ Features:
   yogas can be recognized as "convertible" too, even if they predate the DMI
   "convertible" form factor
 
-* Maybe add a small tool invoked early at boot, that adds in or resizes
-  partitions automatically, to be used when the media used is actually larger
-  than the image written onto it is.
-
 * Maybe add PrivatePIDs= as new unit setting, and do minimal PID namespacing
   after all. Be strict however, only support the equivalent of nspawn's
   --as-pid2 switch, and sanely proxy sd_notify() messages dropping stuff such
@@ -416,24 +528,6 @@ Features:
   "systemd-gdb" for attaching to the start-up of any system service in its
   natural habitat.
 
-* maybe introduce gpt auto discovery for /var/tmp?
-
-* maybe add gpt-partition-based user management: each user gets his own
-  LUKS-encrypted GPT partition with a new GPT type. A small nss module
-  enumerates users via udev partition enumeration. UIDs are assigned in a fixed
-  way: the partition index is added as offset to some fixed base uid. User name
-  is stored in GPT partition name. A PAM module authenticates the user via the
-  LUKS partition password. Benefits: strong per-user security, compatibility
-  with stateless/read-only/verity-enabled root. (other idea: do this based on
-  loopback files in /home, without GPT involvement)
-
-* gpt-auto logic: introduce support for discovering /var matching an image. For
-  that, use a partition type UUID that is hashed from the OS name (as encoded
-  in /etc/os-release), the architecture, and 4 new bits from the gpt flags
-  field of the root partition. This way can easily support multiple OS
-  installations on the same GPT partition table, without problems with
-  unmatched /var partitions.
-
 * gpt-auto logic: related to the above, maybe support a "secondary" root
   partition, that is mounted to / and is writable, and where the actual root's
   /usr is mounted into.
@@ -583,9 +677,6 @@ Features:
 
 * merge ~/.local/share and ~/.local/lib into one similar /usr/lib and /usr/share....
 
-* systemd.show_status= should probably have a mode where only failed
-  units are shown.
-
 * add systemd.abort_on_kill or some other such flag to send SIGABRT instead of SIGKILL
   (throughout the codebase, not only PID1)
 
@@ -803,11 +894,6 @@ Features:
   - journald: when we drop syslog messages because the syslog socket is
     full, make sure to write how many messages are lost as first thing
     to syslog when it works again.
-  - change systemd-journal-flush into a service that stays around during
-    boot, and causes the journal to be moved back to /run on shutdown,
-    so that we do not keep /var busy. This needs to happen synchronously,
-    hence doing this via signals is not going to work.
-  - optionally support running journald from the command line for testing purposes in external projects
   - journald: allow per-priority and per-service retention times when rotating/vacuuming
   - journald: make use of uid-range.h to managed uid ranges to split
     journals in.
index 4526ae2a8ca52e6b3ab2b3fd29cc8aa72ba6c746..40727abaf54a3b05da47314c144049116f6d1297 100644 (file)
@@ -289,11 +289,11 @@ DNS zahtjev ili snimak resursa nije prošao DNSSEC provjeru. To uobičajeno
 označava da je komunikacijski kanal mijenjan.
 
 -- 4d4408cfd0d144859184d1e65d7c8a65
-Subject: DNSSEC pouzdano sidro je opozvano
+Subject: DNSSEC pouzdano sidrište je opozvano
 Defined-By: systemd
 Support: %SUPPORT_URL%
 Documentation: man:systemd-resolved.service(8)
 
-A DNSSEC trust anchor has been revoked. A new trust anchor has to be
-configured, or the operating system needs to be updated, to provide an updated
-DNSSEC trust anchor.
+DNSSEC pouzdano sidrište je opozvano. Novo pouzdano sidrište mora biti
+podešeno, ili operativni sustav mora biti nadopunjen kako bi omogućio nadopunjeno
+DNSSEC pouzdano sidrište.
index a9db8a1cff5e4ed3aaeca0a7613e0701348269d3..5247074b6dd1b25eda3ca4db4f5303438128c5d1 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,4 +1,5 @@
-#!/bin/bash -e
+#!/usr/bin/env bash
+set -e
 
 cflags=CFLAGS="$CFLAGS"
 cxxflags=CXXFLAGS="$CXXFLAGS"
index ba88fe72cc96a21ceac114466d76c7c988000dbd..6084fe2a2a444b080f5c025c69999cc432c53c7b 100644 (file)
@@ -1,5 +1,5 @@
 ---
-title: The Boot Loader Interface
+title: Boot Loader Interface
 category: Booting
 layout: default
 ---
@@ -142,3 +142,11 @@ names for them in UIs.
 6. If a boot menu entry encapsulates a reboot into EFI firmware setup feature,
    it should use the identifier `reboot-to-firmware-setup` (or
    `auto-reboot-to-firmware-setup` in case it is automatically discovered).
+
+## Links
+
+[Boot Loader Specification](https://systemd.io/BOOT_LOADER_INTERFACE)<br>
+[Discoverable Partitions Specification](https://systemd.io/DISCOVERABLE_PARTITIONS)<br>
+[systemd-boot(7)](https://www.freedesktop.org/software/systemd/man/systemd-boot.html)<br>
+[bootctl(1)](https://www.freedesktop.org/software/systemd/man/bootctl.html)<br>
+[systemd-gpt-auto-generator(8)](https://www.freedesktop.org/software/systemd/man/systemd-gpt-auto-generator.html)
index 59c87dd12144bb40ef48e72e024934c3abb75185..514b8cd11afd529cd39028ebfd777eff1a004537 100644 (file)
@@ -1,5 +1,5 @@
 ---
-title: The Boot Loader Specification
+title: Boot Loader Specification
 category: Booting
 layout: default
 ---
@@ -55,14 +55,14 @@ functionality. Here's why we think that it is not enough for our uses:
 
 Everything described below is located on a placeholder file system `$BOOT`. The installer program should pick `$BOOT` according to the following rules:
 
-* On disks with MBR disk labels
-  * If the OS is installed on a disk with MBR disk label, and a partition with the MBR type id of 0xEA already exists it should be used as `$BOOT`.
-  * Otherwise, if the OS is installed on a disk with MBR disk label, a new partition with MBR type id of 0xEA shall be created, of a suitable size (let's say 500MB), and it should be used as `$BOOT`.
-* On disks with GPT disk labels
-  * If the OS is installed on a disk with GPT disk label, and a partition with the GPT type GUID of `bc13c2ff-59e6-4262-a352-b275fd6f7172` already exists, it should be used as `$BOOT`.
-  * Otherwise, if the OS is installed on a disk with GPT disk label, and an ESP partition (i.e. with the GPT type UID of `c12a7328-f81f-11d2-ba4b-00a0c93ec93b`) already exists and is large enough (let's say 250MB`) and otherwise qualifies, it should be used as `$BOOT`.
-  * Otherwise, if the OS is installed on a disk with GPT disk label, and if the ESP partition already exists but is too small, a new suitably sized (let's say 500MB) partition with GPT type GUID of `bc13c2ff-59e6-4262-a352-b275fd6f7172` shall be created and it should be used as `$BOOT`.
-  * Otherwise, if the OS is installed on a disk with GPT disk label, and no ESP partition exists yet, a new suitably sized (let's say 500MB) ESP should be created and should be used as `$BOOT`.
+* On disks with an MBR partition table:
+  * If the OS is installed on a disk with an MBR partition table, and a partition with the type id of 0xEA already exists it should be used as `$BOOT`.
+  * Otherwise, if the OS is installed on a disk with an MBR partition table, a new partition with type id of 0xEA shall be created, of a suitable size (let's say 500MB), and it should be used as `$BOOT`.
+* On disks with GPT (GUID Partition Table)
+  * If the OS is installed on a disk with GPT, and an Extended Boot Loader Partition or XBOOTLDR partition for short, i.e. a partition with GPT type GUID of `bc13c2ff-59e6-4262-a352-b275fd6f7172`, already exists, it should be used as `$BOOT`.
+  * Otherwise, if the OS is installed on a disk with GPT, and an EFI System Partition or ESP for short, i.e. a partition with GPT type UID of `c12a7328-f81f-11d2-ba4b-00a0c93ec93b`) already exists and is large enough (let's say 250MB) and otherwise qualifies, it should be used as `$BOOT`.
+  * Otherwise, if the OS is installed on a disk with GPT, and if the ESP partition already exists but is too small, a new suitably sized (let's say 500MB) XBOOTLDR partition shall be created and used as `$BOOT`.
+  * Otherwise, if the OS is installed on a disk with GPT, and no ESP partition exists yet, a new suitably sized (let's say 500MB) ESP should be created and used as `$BOOT`.
 
 This placeholder file system shall be determined during _installation time_, and an fstab entry may be created. It should be mounted to either `/boot/` or `/efi/`. Additional locations like `/boot/efi/`, with `/boot/` being a separate file system, might be supported by implementations. This is not recommended because the mounting of `$BOOT` is then dependent on and requires the mounting of the intermediate file system.
 
@@ -91,6 +91,20 @@ from the user. Only entries matching the feature set of boot loader and system
 shall be considered and displayed. This allows image builders to put together
 images that transparently support multiple different architectures.
 
+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
+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
+setup. Implementations of this specification must be able to operate correctly
+if files or directories other than `/loader/` are found in the top level
+directory. Implementations that add their own files or directories to the file
+systems should use well-named directories, to make name collisions between
+multiple users of the file system unlikely.
+
 ### Type #1 Boot Loader Specification Entries
 
 We define two directories below `$BOOT`:
@@ -220,5 +234,9 @@ There are a couple of items that are out of focus for this specification:
 
 ## Links
 
+[GUID Partition Table](https://en.wikipedia.org/wiki/GUID_Partition_Table)<br>
+[Boot Loader Interface](https://systemd.io/BOOT_LOADER_INTERFACE)<br>
+[Discoverable Partitions Specification](https://systemd.io/DISCOVERABLE_PARTITIONS)<br>
 [systemd-boot(7)](https://www.freedesktop.org/software/systemd/man/systemd-boot.html)<br>
-[bootctl(1)](https://www.freedesktop.org/software/systemd/man/bootctl.html)
+[bootctl(1)](https://www.freedesktop.org/software/systemd/man/bootctl.html)<br>
+[systemd-gpt-auto-generator(8)](https://www.freedesktop.org/software/systemd/man/systemd-gpt-auto-generator.html)
index da4e38c6550a5d8fcee532c84cee8d1a9814d910..d05503bc97786e0f196b47b8d746f5ae44834977 100644 (file)
@@ -21,10 +21,10 @@ comprehensive up-to-date information about all this, particular in light of the
 poor implementations of the components interfacing with systemd of current
 container managers.
 
-Before you read on, please make sure you read the low-level [kernel
-documentation about
-cgroup v2](https://www.kernel.org/doc/Documentation/cgroup-v2.txt). This
-documentation then adds in the higher-level view from systemd.
+Before you read on, please make sure you read the low-level kernel
+documentation about the
+[unified cgroup hierarchy](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html).
+This document then adds in the higher-level view from systemd.
 
 This document augments the existing documentation we already have:
 
index b0925691c9958db5350b99688ecdef352627a5fa..b906bf5acb58da7d766edd8f85c9c37801e81edf 100644 (file)
@@ -1,5 +1,5 @@
 ---
-title: The systemd Community Conduct Guidelines
+title: systemd Community Conduct Guidelines
 category: Contributing
 layout: default
 ---
index fc5c0a06edc1a5742bc41b5c3fa4a30ccb6eff84..71f9185c585408464489a6d139076ffc9006e488 100644 (file)
@@ -1,5 +1,5 @@
 ---
-title: The Container Interface
+title: Container Interface
 category: Interfaces
 layout: default
 ---
diff --git a/docs/DISCOVERABLE_PARTITIONS.md b/docs/DISCOVERABLE_PARTITIONS.md
new file mode 100644 (file)
index 0000000..f1537b8
--- /dev/null
@@ -0,0 +1,216 @@
+---
+title: Discoverable Partitions Specification
+category: Concepts
+layout: default
+---
+# The Discoverable Partitions Specification
+
+_TL;DR: Let's automatically discover, mount and enable the root partition,
+`/home/`, `/srv/`, `/var/` and `/var/tmp/` and the swap partitions based on
+GUID Partition Tables (GPT)!_
+
+The GUID Partition Table (GPT) is mandatory on EFI systems. It allows
+identification of partition types with UUIDs. So far Linux has made little use
+of this, and mostly just defined one UUID for file system/data partitions and
+another one for swap partitions.  With this specification, we introduce
+additional partition types to enable automatic discovery of partitions and
+their intended mountpoint.  This has many benefits:
+
+* OS installers can automatically discover and make sense of partitions of
+  existing Linux installations.
+* The OS can discover and mount the necessary file systems with a non-existing
+  or incomplete `/etc/fstab` file and without the `root=` kernel command line
+  option.
+* Container managers (such as nspawn and libvirt-lxc) can decode and set up
+  file systems contained in GPT disk images automatically and mount them to the
+  right places, thus allowing booting the same, identical images on bare-metal
+  and in Linux containers. This enables true, natural portability of disk
+  images between physical machines and Linux containers.
+* As a help to administrators and users partition manager tools can show more
+  descriptive information about partitions tables.
+
+Note that the OS side of this specification is currently implemented in
+[systemd](http://systemd.io/) 211 and newer in the
+[systemd-auto-gpt-generator(8)](http://www.freedesktop.org/software/systemd/man/systemd-gpt-auto-generator.html)
+generator tool. Note that automatic discovery of the root only works if the
+boot loader communicates this information to the OS, by implementing the [Boot
+Loader
+Interface](https://systemd.io/BOOT_LOADER_INTERFACE).
+
+## Defined Partition Type UUIDs
+
+| Partition Type UUID | Name | Allowed File Systems | Explanation |
+|---------------------|------|----------------------|-------------|
+| `44479540-f297-41b2-9af7-d131d5f0458a` | _Root Partition (x86)_ | Any native, optionally in LUKS | On systems with matching architecture, the first partition with this type UUID on the disk containing the active EFI ESP is automatically mounted to the root directory <tt>/</tt>. If the partition is encrypted with LUKS or has dm-verity integrity data (see below), the device mapper file will be named `/dev/mapper/root`. |
+| `4f68bce3-e8cd-4db1-96e7-fbcaf984b709` | _Root Partition (x86-64)_ | ditto | ditto |
+| `69dad710-2ce4-4e3c-b16c-21a1d49abed3` | _Root Partition (32-bit ARM)_ | ditto | ditto |
+| `b921b045-1df0-41c3-af44-4c6f280d3fae` | _Root Partition (64-bit ARM/AArch64)_ | ditto | ditto |
+| `993d8d3d-f80e-4225-855a-9daf8ed7ea97` | _Root Partition (Itanium/IA-64)_ | ditto | ditto |
+| `d13c5d3b-b5d1-422a-b29f-9454fdc89d76` | _Root Verity Partition (x86)_ | A dm-verity superblock followed by hash data | On systems with matching architecture, contains dm-verity integrity hash data for the matching root partition. If this feature is used the partition UUID of the root partition should be the first 128bit of the root hash of the dm-verity hash data, and the partition UUID of this dm-verity partition should be the final 128bit of it, so that the root partition and its verity partition can be discovered easily, simply by specifying the root hash. |
+| `2c7357ed-ebd2-46d9-aec1-23d437ec2bf5` | _Root Verity Partition (x86-64)_ | ditto | ditto |
+| `7386cdf2-203c-47a9-a498-f2ecce45a2d6` | _Root Verity Partition (32-bit ARM)_ | ditto | ditto |
+| `df3300ce-d69f-4c92-978c-9bfb0f38d820` | _Root Verity Partition (64-bit ARM/AArch64)_ | ditto | ditto |
+| `86ed10d5-b607-45bb-8957-d350f23d0571` | _Root Verity Partition (Itanium/IA-64)_  | ditto | ditto |
+| `933ac7e1-2eb4-4f13-b844-0e14e2aef915` | _Home Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/home/`.  If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/home`. |
+| `3b8f8425-20e0-4f3b-907f-1a25a76f98e8` | _Server Data Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/srv/`.  If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/srv`. |
+| `4d21b016-b534-45c2-a9fb-5c16e091fd2d` | _Variable Data Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/var/` — under the condition that its partition UUID matches the first 128 bit of `HMAC-SHA256(machine-id, 0x4d21b016b53445c2a9fb5c16e091fd2d)` (i.e. the SHA256 HMAC hash of the binary type UUID keyed by the machine ID as read from [`/etc/machine-id`](https://www.freedesktop.org/software/systemd/man/machine-id.html). This special requirement is made because `/var/` (unlike the other partition types listed here) is inherently private to a specific installation and cannot possibly be shared between multiple OS installations on the same disk, and thus should be bound to a specific instance of the OS, identified by its machine ID. If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/var`. |
+| `7ec6f557-3bc5-4aca-b293-16ef5df639d1` | _Temporary Data Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/var/tmp/`.  If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/tmp`. Note that the intended mount point is indeed `/var/tmp/`, not `/tmp/`. The latter is typically maintained in memory via <tt>tmpfs</tt> and does not require a partition on disk. In some cases it might be desirable to make `/tmp/` persistent too, in which case it is recommended to make it a symlink or bind mount to `/var/tmp/`, thus not requiring its own partition type UUID. |
+| `0657fd6d-a4ab-43c4-84e5-0933c84b4f4f` | _Swap_ | Swap | All swap partitions on the disk containing the root partition are automatically enabled. |
+| `c12a7328-f81f-11d2-ba4b-00a0c93ec93b` | _EFI System Partition_ | VFAT | The ESP used for the current boot is automatically mounted to `/efi/` (or `/boot/` as fallback), unless a different partition is mounted there (possibly via `/etc/fstab`, or because the Extended Boot Loader Partition — see below — exists) or the directory is non-empty on the root disk.  This partition type is defined by the [UEFI Specification](http://www.uefi.org/specifications). |
+| `bc13c2ff-59e6-4262-a352-b275fd6f7172` | _Extended Boot Loader Partition_ | Typically VFAT | The Extended Boot Loader Partition (XBOOTLDR) used for the current boot is automatically mounted to <tt>/boot/</tt>, unless a different partition is mounted there (possibly via <tt>/etc/fstab</tt>) or the directory is non-empty on the root disk. This partition type is defined by the [Boot Loader Specification](https://systemd.io/BOOT_LOADER_SPECIFICATION). |
+| `0fc63daf-8483-4772-8e79-3d69d8477de4` | _Other Data Partitions_ | Any native, optionally in LUKS | No automatic mounting takes place for other Linux data partitions. This partition type should be used for all partitions that carry Linux file systems. The installer needs to mount them explicitly via entries in <tt>/etc/fstab</tt>. Optionally, these partitions may be encrypted with LUKS. |
+
+Other GPT type IDs might be used on Linux, for example to mark software RAID or
+LVM partitions. The definitions of those GPT types is outside of the scope of
+this specification.
+
+[systemd-id128(1)](http://www.freedesktop.org/software/systemd/man/systemd-i128.html)
+may be used to list those UUIDs.
+
+## Partition Names
+
+For partitions of the types listed above it is recommended to use
+human-friendly, descriptive partition names in the GPT partition table, for
+example "*Home*", "*Server* *Data*", "*Fedora* *Root*" and similar, possibly
+localized.
+
+## Partition Flags
+
+For the root, server data, home, variable data, temporary data and swap
+partitions, the partition flag bit 63 ("*no-auto*") may be used to turn off
+auto-discovery for the specific partition.  If set, the partition will not be
+automatically mounted or enabled.
+
+For the root, server data, home, variable data and temporary data partitions,
+the partition flag bit 60 ("*read-only*") may be used to mark a partition for
+read-only mounts only.  If set, the partition will be mounted read-only instead
+of read-write. Note that the variable data partition and the temporary data
+partition will generally not be able to serve their purpose if marked
+read-only, since by their very definition they are supposed to be mutable. (The
+home and server data partitions are generally assumed to be mutable as well,
+but the requirement for them is not equally strong.) Because of that, while the
+read-only flag is defined and supported, it's almost never a good idea to
+actually use it for these partitions.
+
+Note that these two flag definitions happen to map nicely to the ones used by
+Microsoft Basic Data Partitions.
+
+## Suggested Mode of Operation
+
+An *installer* that repartitions the hard disk _should_ use the above UUID
+partition types for appropriate partitions it creates.
+
+An *installer* which supports a "manual partitioning" interface _may_ choose to
+pre-populate the interface with swap, `/home/`, `/srv/`, `/var/tmp/` partitions
+of pre-existing Linux installations, identified with the GPT type UUIDs
+above. The installer should not pre-populate such an interface with any
+identified root or `/var/` partition unless the intention is to overwrite an
+existing operating system that might be installed.
+
+An *installer* _may_ omit creating entries in `/etc/fstab` for root, `/home/`,
+`/srv/`, `/var/`, `/var/tmp` and for the swap partitions if they use these UUID
+partition types, and are the first partitions on the disk of each type. If the
+ESP shall be mounted to `/efi/` (or `/boot/`), it may additionally omit
+creating the entry for it in `/etc/fstab`.  If an extended boot partition is
+used, or if the EFI partition shall not be mounted to `/efi/` or `/boot/`, it
+_must_ create `/etc/fstab` entries for them.  If other partitions are used (for
+example for `/usr/` or `/var/lib/mysql/`), the installer _must_ register these
+in `/etc/fstab`.  The `root=` parameter passed to the kernel by the boot loader
+may be omitted if the root partition is the first one on the disk of its type.
+If the root partition is not the first one on the disk, the `root=` parameter
+_must_ be passed to the kernel by the boot loader.  An installer that mounts a
+root, `/home/`, `/srv/`, `/var/`, or `/var/tmp/` file system with the partition
+types defined as above which contains a LUKS header _must_ call the device
+mapper device "root", "home", "srv", "var" or "tmp", respectively.  This is
+necessary to ensure that the automatic discovery will never result in different
+device mapper names than any static configuration by the installer, thus
+eliminating possible naming conflicts and ambiguities.
+
+An *operating* *system* _should_ automatically discover and mount the first
+root partition that does not have the no-auto flag set (as described above) by
+scanning the disk containing the currently used EFI ESP.  It _should_
+automatically discover and mount the first `/home/`, `/srv/`, `/var/`,
+`/var/tmp/` and swap partitions that do not have the no-auto flag set by
+scanning the disk containing the discovered root partition.  It should
+automatically discover and mount the partition containing the currently used
+EFI ESP to `/efi/` (or `/boot/` as fallback).  It should automatically discover
+and mount the partition containing the currently used Extended Boot Loader
+Partition to `/boot/`. It _should not_ discover or automatically mount
+partitions with other UUID partition types, or partitions located on other
+disks, or partitions with the no-auto flag set.  User configuration shall
+always override automatic discovery and mounting.  If a root, `/home/`,
+`/srv/`, `/boot/`, `/var/`, `/var/tmp/`, `/efi/`, `/boot/` or swap partition is
+listed in `/etc/fstab` or with `root=` on the kernel command line, it _must_
+take precedence over automatically discovered partitions.  If a `/home/`,
+`/srv/`, `/boot/`, `/var/`, `/var/tmp/`, `/efi/` or `/boot/` directory is found
+to be populated already in the root partition, the automatic discovery _must
+not_ mount any discovered file system over it.
+
+A *container* *manager* should automatically discover and mount the root,
+`/home/`, `/srv/`, `/var/`, `/var/tmp/` partitions inside a container disk
+image.  It may choose to mount any discovered ESP and/or XBOOOTLDR partition to
+`/efi/` or `/boot/`. It should ignore any swap should they be included in a
+container disk image.
+
+If a btrfs file system is automatically discovered and mounted by the operating
+system/container manager it will be mounted with its *default* subvolume.  The
+installer should make sure to set the default subvolume correctly using "btrfs
+subvolume set-default".
+
+## Sharing of File Systems between Installations
+
+If two Linux-based operating systems are installed on the same disk, the scheme
+above suggests that they may share the swap, `/home/`, `/srv/`, `/var/tmp/`,
+ESP, XBOOTLDR. However, they should each have their own root and `/var/`
+partition.
+
+## Frequently Asked Questions
+
+### Why are you taking my `/etc/fstab` away?
+
+We are not. `/etc/fstab` always overrides automatic discovery and is indeed
+mentioned in the specifications.  We are simply trying to make the boot and
+installation processes of Linux a bit more robust and self-descriptive.
+
+### Why did you only define the root partition for x86, x86-64, ARM, ARM64, ia64?
+
+The automatic discovery of the root partition is defined to operate on the disk
+containing the current EFI System Partition (ESP). Since EFI only exists on
+x86, x86-64, ia64, and ARM so far, we only defined root partition UUIDs for
+these architectures.  Should EFI become more common on other architectures, we
+can define additional UUIDs for them.
+
+### Why define distinct root partition UUIDs for the various architectures?
+
+This allows disk images that may be booted on multiple architectures to use
+discovery of the appropriate root partition on each architecture.
+
+### Doesn't this break multi-boot scenarios?
+
+No, it doesn't.  The specification says that installers may not stop creating
+`/etc/fstab` or stop including `root=` on the kernel command line, unless the used
+partitions are the first ones of their type on the disk. Additionally,
+`/etc/fstab` and `root=` both override automatic discovery.  Multi-boot is hence
+well supported, since it doesn't change anything for anything but the first
+installation.
+
+That all said, it's not expected that generic installers generally stop setting
+`root=` and creating `/etc/fstab` anyway. The option to drop these configuration
+bits is primarily something for appliance-like devices.  However, generic
+installers should *still* set the right GPT partition types for the partitions
+they create so that container managers, partition tools and administrators can
+benefit.  Phrased differently, this specification introduces A) the
+*recommendation* to use the newly defined partition types to tag things
+properly and B) the *option* to then drop `root=` and `/etc/fstab`.  While we
+advertise A) to *all* installers, we only propose B) for simpler,
+appliance-like installations.
+
+### What partitioning tools will create a DPS-compliant partition table?
+
+As of util-linux 2.25.2, the fdisk tool provides type codes to create the root,
+home, and swap partitions that the DPS expects, but the gdisk tool (version
+0.8.10) and its variants do not support creation of a root file system with a
+matching type code.  By default, fdisk will create an old-style MBR, not a GPT,
+so typing 'l' to list partition types will not show the choices that the root
+partition with the correct UUID.  You must first create an empty GPT and then
+type 'l' in order for the DPS-compliant type codes to be available.
index 9b1df6b59cb183a6a7145e808358d9eb71d78f41..fec40b1e9ca7528f796c2023137d120697e100c0 100644 (file)
@@ -73,6 +73,13 @@ All tools:
   appropriate path under /run. This variable is also set by the manager when
   RuntimeDirectory= is used, see systemd.exec(5).
 
+* `$SYSTEMD_CRYPT_PREFIX` — if set configures the hash method prefix to use for
+  UNIX crypt() when generating passwords. By default the system's "preferred
+  method" is used, but this can be overridden with this environment
+  variable. Takes a prefix such as `$6$` or `$y$`. (Note that this is only
+  honoured on systems built with libxcrypt and is ignored on systems using
+  glibc's original, internal crypt() implementation.)
+
 systemctl:
 
 * `$SYSTEMCTL_FORCE_BUS=1` — if set, do not connect to PID1's private D-Bus
diff --git a/docs/GROUP_RECORD.md b/docs/GROUP_RECORD.md
new file mode 100644 (file)
index 0000000..8f98b49
--- /dev/null
@@ -0,0 +1,158 @@
+---
+title: JSON Group Records
+category: Interfaces
+layout: default
+---
+
+# JSON Group Records
+
+Long story short: JSON Group Records are to `struct group` what [JSON User
+Records](https://systemd.io/USER_RECORD.md) are to `struct passwd`.
+
+Conceptually, much of what applies to JSON user records also applies to JSON
+group records. They also consist of seven sections, with similar properties and
+they carry some identical (or at least very similar) fields.
+
+## Fields in the `regular` section
+
+`groupName` → A string with the UNIX group name. Matches the `gr_name` field of
+UNIX/glibc NSS `struct group`, or the shadow structure `struct sgrp`'s
+`sg_namp` field.
+
+`realm` → The "realm" the group belongs to, conceptually identical to the same
+field of user records. A string in DNS domain name syntax.
+
+`disposition` → The disposition of the group, conceptually identical to the
+same field of user records. A string.
+
+`service` → A string, an identifier for the service managing this group record
+(this field is typically in reverse domain name syntax.)
+
+`lastChangeUSec` → An unsigned 64bit integer, a timestamp (in µs since the UNIX
+epoch 1970) of the last time the group record has been modified. (Covers only
+the `regular`, `perMachine` and `privileged` sections).
+
+`gid` → An unsigned integer in the range 0…4294967295: the numeric UNIX group
+ID (GID) to use for the group. This corresponds to the `gr_gid` field of
+`struct group`.
+
+`members` → An array of strings, listing user names that are members of this
+group. Note that JSON user records also contain a `memberOf` field, or in other
+words a group membership can either be denoted in the JSON user record or in
+the JSON group record, or in both. The list of memberships should be determined
+as the combination of both lists (plus optionally others). If a user is listed
+as member of a group and doesn't exist it should be ignored. This field
+corresponds to the `gr_mem` field of `struct group` and the `sg_mem` field of
+`struct sgrp`.
+
+`administrators` → Similarly, an array of strings, listing user names that
+shall be considered "administrators" of this group. This field corresponds to
+the `sg_adm` field of `struct sgrp`.
+
+`privileged`/`perMachine`/`binding`/`status`/`signature`/`secret` → The
+objects/arrays for the other six group record sections. These are organized the
+same way as for the JSON user records, and have the same semantics.
+
+## Fields in the `privileged` section
+
+The following fields are defined:
+
+`hashedPassword` → An array of strings with UNIX hashed passwords; see the
+matching field for user records for details. This field corresponds to the
+`sg_passwd` field of `struct sgrp` (and `gr_passwd` of `struct group` in a
+way).
+
+## Fields in the `perMachine` section
+
+`matchMachineId`/`matchHostname` → Strings, match expressions similar as for
+user records, see the user record documentation for details.
+
+The following fields are defined for the `perMachine` section and are defined
+equivalent to the fields of the same name in the `regular` section, and
+override those:
+
+`gid`, `members`, `administrators`
+
+## Fields in the `binding` section
+
+The following fields are defined for the `binding` section, and are equivalent
+to the fields of the same name in the `regular` and `perMachine` sections:
+
+`gid`
+
+## Fields in the `status` section
+
+The following fields are defined in the `status` section, and are mostly
+equivalent to the fields of the same name in the `regular` section, though with
+slightly different conceptual semantics, see the same fields in the user record
+documentation:
+
+`service`
+
+## Fields in the `signature` section
+
+The fields in this section are defined identically to those in the matching
+section in the user record.
+
+## Fields in the `secret` section
+
+Currently no fields are defined in this section for group records.
+
+## Mapping to `struct group` and `struct sgrp`
+
+When mapping classic UNIX group records (i.e. `struct group` and `struct sgrp`)
+to JSON group records the following mappings should be applied:
+
+| Structure      | Field       | Section      | Field            | Condition                  |
+|----------------|-------------|--------------|------------------|----------------------------|
+| `struct group` | `gr_name`   | `regular`    | `groupName`      |                            |
+| `struct group` | `gr_passwd` | `privileged` | `password`       | (See notes below)          |
+| `struct group` | `gr_gid`    | `regular`    | `gid`            |                            |
+| `struct group` | `gr_mem`    | `regular`    | `members`        |                            |
+| `struct sgrp`  | `sg_namp`   | `regular`    | `groupName`      |                            |
+| `struct sgrp`  | `sg_passwd` | `privileged` | `password`       | (See notes below)          |
+| `struct sgrp`  | `sg_adm`    | `regular`    | `administrators` |                            |
+| `struct sgrp`  | `sg_mem`    | `regular`    | `members`        |                            |
+
+At this time almost all Linux machines employ shadow passwords, thus the
+`gr_passwd` field in `struct group` is set to `"x"`, and the actual password
+is stored in the shadow entry `struct sgrp`'s field `sg_passwd`.
+
+## Extending These Records
+
+The same logic and recommendations apply as for JSON user records.
+
+## Examples
+
+A reasonable group record for a system group might look like this:
+
+```json
+{
+       "groupName" : "systemd-resolve",
+       "gid" : 193,
+       "status" : {
+               "6b18704270e94aa896b003b4340978f1" : {
+                       "service" : "io.systemd.NameServiceSwitch"
+               }
+       }
+}
+```
+
+And here's a more complete one for a regular group:
+
+```json
+{
+       "groupName" : "grobie",
+       "binding" : {
+               "6b18704270e94aa896b003b4340978f1" : {
+                       "gid" : 60232
+               }
+       },
+       "disposition" : "regular",
+       "status" : {
+               "6b18704270e94aa896b003b4340978f1" : {
+                       "service" : "io.systemd.Home"
+               }
+       }
+}
+```
index db9ed1a01ebd0b6fb03819b64f32b4260fa24e75..c0516b5c624affe14f98df38fdf378766cc1a091 100644 (file)
@@ -127,7 +127,5 @@ guidance in [CONTRIBUTING.md](CONTRIBUTING.md) on how to report a security vulne
 
 For more details on building fuzzers and integrating with OSS-Fuzz, visit:
 
-- https://github.com/google/oss-fuzz/blob/master/docs/new_project_guide.md
-- https://llvm.org/docs/LibFuzzer.html
-- https://github.com/google/fuzzer-test-suite/blob/master/tutorial/libFuzzerTutorial.md
-- https://chromium.googlesource.com/chromium/src/testing/libfuzzer/+/HEAD/efficient_fuzzer.md
+- [Setting up a new project - OSS-Fuzz](https://google.github.io/oss-fuzz/getting-started/new-project-guide/)
+- [Tutorials - OSS-Fuzz](https://google.github.io/oss-fuzz/reference/useful-links/#tutorials)
diff --git a/docs/HOME_DIRECTORY.md b/docs/HOME_DIRECTORY.md
new file mode 100644 (file)
index 0000000..73c2359
--- /dev/null
@@ -0,0 +1,173 @@
+---
+title: Home Directories
+category: Concepts
+layout: default
+---
+
+# Home Directories
+
+[`systemd-homed.service(8)`](https://www.freedesktop.org/software/systemd/man/systemd-homed.service.html)
+manages home directories of regular ("human") users. Each directory it manages
+encapsulates both the data store and the user record of the user so that it
+comprehensively describes the user account, and is thus naturally portable
+between systems without any further, external metadata. This document describes
+the format used by these home directories, in context of the storage mechanism
+used.
+
+## General Structure
+
+Inside of the home directory a file `~/.identity` contains the JSON formatted
+user record of the user. It follows the format defined in [`JSON User
+Records`](https://systemd.io/USER_RECORD). It is recommended to bring the
+record into 'normalized' form (i.e. all objects should contain their fields
+sorted alphabetically by their key) before storing it there, though this is not
+required nor enforced. Since the user record is cryptographically signed the
+user cannot make modifications to the file on their own (at least not without
+corrupting it, or knowing the private key used for signing the record). Note
+that user records are stored here without their `binding`, `status` and
+`secret` sections, i.e. only with the sections included in the signature plus
+the signature section itself.
+
+## Storage Mechanism: Plain Directory/`btrfs` Subvolume
+
+If the plain directory or `btrfs` subvolume storage mechanism of
+`systemd-homed` is used (i.e. `--storage=directory` or `--storage=subvolume` on
+the
+[`homectl(1)`](https://www.freedesktop.org/software/systemd/man/homectl.html)
+command line) the home directory requires no special set-up besides including
+the user record in the `~/.identity` file.
+
+It is recommended to name home directories managed this way by
+`systemd-homed.service` by the user name, suffixed with `.homedir` (example:
+`lennart.homedir` for a user `lennart`) but this is not enforced. When the user
+is logged in the directory is generally mounted to `/home/$USER` (in our
+example: `/home/lennart`), thus dropping the suffix while the home directory is
+active. `systemd-homed` will automatically discover home directories named this
+way in `/home/*.homedir` and synthesize NSS user records for them as they show
+up.
+
+## Storage Mechanism: `fscrypt` Directories
+
+This storage mechanism is mostly identical to the plain directory storage
+mechanism, except that the home directory is encrypted using `fscrypt`. (Use
+`--storage=fscrypt` on the `homectl` command line.) Key management is
+implemented via extended attributes on the directory itself: for each password
+an extended attribute `trusted.fscrypt_slot0`, `trusted.fscrypt_slot1`,
+`trusted.fscrypt_slot2`, … is maintained. It's value contains a colon-separated
+pair of Base64 encoded data fields. The first field contains a salt value, the
+second field the encrypted volume key. The latter is encrypted using AES256 in
+counter mode, using a key derived from the password via PBKDF2-HMAC-SHA512
+together with the salt value. The construction is similar to what LUKS does for
+`dm-crypt` encrypted volumes. Note that extended attributes are not encrypted
+by `fscrypt` and hence are suitable for carry the key slots. Moreover, by using
+extended attributes the slots are directly attached to the directory and an
+independent sidecar key database is not required.
+
+## Storage Mechanism: `cifs` Home Directories
+
+In this storage mechanism the home directory is mounted from a CIFS server and
+service at login, configured inside the user record. (Use `--storage=cifs` on
+the `homectl` command line.) The local password of the user is used to log into
+the CIFS service. The directory share needs to contain the user record in
+`~/.identity` as well. Note that this means that the user record needs to be
+registered locally before it can be mounted for the first time, since CIFS
+domain and server information needs to be known *before* the mount. Note that
+for all other storage mechanisms it is entirely sufficient if the directories
+or storage artifacts are placed at the right locations — all information to
+activate them can be derived automatically from their mere availability.
+
+## Storage Mechanism: `luks` Home Directories
+
+This is the most advanced and most secure storage mechanism and consists of a
+Linux file system inside a LUKS2 volume inside a loopback file (or on removable
+media). (Use `--storage=luks` on the `homectl` command line.)  Specifically:
+
+* The image contains a GPT partition table. For now it should only contain a
+  single partition, and that partition must have the type UUID
+  `773f91ef-66d4-49b5-bd83-d683bf40ad16`. It's partition label must be the
+  user name.
+
+* This partition must contain a LUKS2 volume, whose label must be the user
+  name. The LUKS2 volume must contain a LUKS2 token field of type
+  `systemd-homed`. The JSON data of this token must have a `record` field,
+  containing a string with base64-encoded data. This data is the JSON user
+  record, in the same serialization as in `~/.identity`, though encrypted. The
+  JSON data of this token must also have an `iv` field, which contains a
+  base64-encoded binary initialization vector for the encryption. The
+  encryption used is the same as the LUKS2 volume itself uses, unlocked by the
+  same volume key, but based on its own IV.
+
+* Inside of this LUKS2 volume must be a Linux file system, one of `ext4`,
+  `btrfs` and `xfs`. The file system label must be the user name.
+
+* This file system should contain a single directory named after the user. This
+  directory will become the home directory of the user when activated. It
+  contains a second copy of the user record in the `~/.identity` file, like in
+  the other storage mechanisms.
+
+The image file should either reside in a directory `/home/` on the system,
+named after the user, suffixed with `.home`. When activated the container home
+directory is mounted to the same path, though with the `.home` suffix dropped —
+unless a different mount point is defined in the user record. (e.g.: the
+loopback file `/home/waldo.home` is mounted to `/home/waldo` while activated.)
+When the image is stored on removable media (such as a USB stick) the image
+file can be directly `dd`'ed onto it, the format is unchanged. The GPT envelope
+should ensure the image is properly recognizable as a home directory both when
+used in a loopback file and on a removable USB stick. (Note that when mounting
+a home directory from an USB stick it too defaults to a directory in `/home/`,
+named after the username, with no further suffix.)
+
+Rationale for the GPT partition table envelope: this way the image is nicely
+discoverable and recognizable already by partition managers as a home
+directory. Moreover, when copied onto a USB stick the GPT envelope makes sure
+the stick is properly recognizable as a portable home directory
+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:
+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
+necessary to validate the home directory image *before* mounting it and
+establishing a minimal level of trust. Since the user record data is
+cryptographically signed and user records not signed with a recognized private
+key are not accepted a minimal level of trust between the system and the home
+directory image is established.
+
+Rationale for storing the home directory one level below to root directory of
+the contained file system: this way special directories such as `lost+found/`
+do not show up in the user's home directory.
+
+## Algorithm
+
+Regardless of the storage mechanism used, an activated home directory
+necessarily involves a mount point to be established. In case of the
+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
+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
+the user record are applied when the bind mount is established.
+
+During activation, the user records retained on the host, the user record
+stored in the LUKS2 header (in case of the LUKS2 storage mechanism) and the
+user record stored inside the home directory in `~/.identity` are
+compared. Activation is only permitted if they match the same user and are
+signed by a recognized key. When the three instances differ in `lastChangeUSec`
+field, the newest record wins, and is propagated to the other two locations.
+
+During activation the file system checker (`fsck`) appropriate for the
+selected file system is automatically invoked, ensuring the file system is in a
+healthy state before it is mounted.
+
+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
+loopback file is `fallocate()`ed during activation, or the mounted file system
+is `FITRIM`ed after mounting, to ensure the setting is correctly enforced.
index 04b189f89cd0b607a1cbccb6da0b5a60f297a8be..8985f2761c8faa8c06aba80ea14527f424a3c74e 100644 (file)
@@ -19,7 +19,7 @@ interfaces are currently used by dracut and the ArchLinux initrds.
 
 * The initrd should mount `/run/` as a tmpfs and pass it pre-mounted when
   jumping into the main system when executing systemd. The mount options should
-  be `mode=755,nodev,nosuid,strictatime`
+  be `mode=755,nodev,nosuid,strictatime`.
 
 * It's highly recommended that the initrd also mounts `/usr/` (if split off) as
   appropriate and passes it pre-mounted to the main system, to avoid the
index d98358957b19642a1d2526b831e31a5a0f686647..95bfcb98d38b291e5d7828ad7c4c2185546418ec 100644 (file)
@@ -4,7 +4,7 @@ category: Interfaces
 layout: default
 ---
 
-# Interface Stability Promise
+# Interface Portability and Stability Promise
 
 systemd provides various interfaces developers and programs might rely on. Starting with version 26 (the first version released with Fedora 15) we promise to keep a number of them stable and compatible for the future.
 
@@ -18,7 +18,7 @@ The stable interfaces are:
 
 * Some of the **"special" unit names** and their semantics. To be precise the ones that are necessary for normal services, and not those required only for early boot and late shutdown, with very few exceptions. To list them here: `basic.target`, `shutdown.target`, `sockets.target`, `network.target`, `getty.target`, `graphical.target`, `multi-user.target`, `rescue.target`, `emergency.target`, `poweroff.target`, `reboot.target`, `halt.target`, `runlevel[1-5].target`.
 
-* **The D-Bus interfaces of the main service daemon and other daemons**. We try to always preserve backwards compatiblity, and intentational breakage is never introduced. Nevertheless, when we find bugs that mean that the existing interface was not useful, or when the implementation did something different than stated by the documentation and the implemented behaviour is not useful, we will fix the implementation and thus introduce a change in behaviour. But the API (parameter counts and types) is never changed, and existing attributes and methods will not be removed.
+* **The D-Bus interfaces of the main service daemon and other daemons**. We try to always preserve backwards compatibility, and intentional breakage is never introduced. Nevertheless, when we find bugs that mean that the existing interface was not useful, or when the implementation did something different than stated by the documentation and the implemented behaviour is not useful, we will fix the implementation and thus introduce a change in behaviour. But the API (parameter counts and types) is never changed, and existing attributes and methods will not be removed.
 
 * For a more comprehensive and authoritative list, consult the chart below.
 
@@ -41,7 +41,7 @@ What does this mean for you? When developing with systemd, don't use any of the
 Note that this is a promise, not an eternal guarantee. These are our intentions, but if in the future there are very good reasons to change or get rid of an interface we have listed above as stable, then we might take the liberty to do so, despite this promise. However, if we do this, then we'll do our best to provide a smooth and reasonably long transition phase.
 
 
-# Interface Portability And Stability Chart
+## Interface Portability And Stability Chart
 
 systemd provides a number of APIs to applications. Below you'll find a table detailing which APIs are considered stable and how portable they are.
 
@@ -72,11 +72,9 @@ A number of systemd's APIs expose Linux or systemd-specific features that cannot
 Note that not all of these interfaces are our invention (but most), we just adopted them in systemd to make them more prominently implemented. For example, we adopted many Debian facilities in systemd to push it into the other distributions as well.
 
 
-
 ---
 
 
-
 And now, here's the list of (hopefully) all APIs that we have introduced with systemd:
 
 | API  | Type | Covered by Interface Stability Promise | Fully documented | Known External Consumers | Reimplementable Independently | Known Other Implementations | systemd Implementation portable to other OSes or non-systemd distributions |
@@ -98,6 +96,8 @@ And now, here's the list of (hopefully) all APIs that we have introduced with sy
 | [$NOTIFY_SOCKET Daemon Notifications](https://www.freedesktop.org/software/systemd/man/sd_notify.html) | Environment | yes | yes | a few, including udev | yes | - | no |
 | [argv&#91;0&#93;&#91;0&#93;='@' Logic](https://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons) | `/proc` marking | yes | yes | mdadm | yes | - | no |
 | [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 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 |
@@ -136,9 +136,9 @@ This is not an attempt to comprehensively list all users of these APIs. We are j
 
 Of course, one last thing I can't make myself not ask you before we finish here, and before you start reimplementing these APIs in your distribution: are you sure it's time well spent if you work on reimplementing all this code instead of just spending it on adopting systemd on your distro as well?
 
-## Independent operation of systemd programs
+## Independent Operation of systemd Programs
 
-Some programs in the systemd suite are indended to operate independently of the
+Some programs in the systemd suite are intended to operate independently of the
 running init process (or even without an init process, for example when
 creating system installation chroots). They can be safely called on systems with
 a different init process or for example in package installation scriptlets.
@@ -155,6 +155,8 @@ without communicating with the `systemd` process:
 Many other programs support operation without the system manager except when
 the specific functionality requires such communication. For example
 `journalctl` operates almost independently, but will query the boot id when
-`--boot` option is used. `systemd-journal-remote`, `systemd-journal-upload`,
-`systemd-journal-gatewayd`, `coredumpctl`, `busctl`, `systemctl --root` also
-fall into this category.
+`--boot` option is used; it also requires `systemd-journald` (and thus
+`systemd`) to be running for options like `--flush` and `--sync`.
+`systemd-journal-remote`, `systemd-journal-upload`, `systemd-journal-gatewayd`,
+`coredumpctl`, `busctl`, `systemctl --root` also fall into this category of
+mostly-independent programs.
index d9c838f98263f7355d316174f5b783805b034ae9..8248275cedc6f097fe87cf0c2df3d9db11ffbc79 100644 (file)
@@ -165,7 +165,7 @@ requirements are made for an image that can be attached/detached with
    an image with a partition table understood by the Linux kernel with only a
    single partition defined, or alternatively, a GPT partition table with a set
    of properly marked partitions following the [Discoverable Partitions
-   Specification](https://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/).
+   Specification](https://systemd.io/DISCOVERABLE_PARTITIONS).
 
 3. The image must at least contain one matching unit file, with the right name
    prefix and suffix (see above). The unit file is searched in the usual paths,
index 451034dc76dde2a32bf11fbd3765293f7d869460..779044b0d4e8358134a20c7ec99624e5c2bc8bf3 100644 (file)
@@ -87,12 +87,12 @@ systemd 38:
 Processes (run by the root user) whose first character of the zeroth command
 line argument is `@` are excluded from the killing spree, much the same way as
 kernel threads are excluded too. Thus, a daemon which wants to take advantage
-of this logic needs to place the following at the top of its main() function:
+of this logic needs to place the following at the top of its `main()` function:
 
 ```c
-…
-[0][0] = '@';
-…
+...
+argv[0][0] = '@';
+...
 ```
 
 And that's already it. Note that this functionality is only to be used by
@@ -116,10 +116,10 @@ otherwise doesn't. Something like this:
 #include <unistd.h>
 
 int main(int argc, char *argv[]) {
-        …
+        ...
         if (access("/etc/initrd-release", F_OK) >= 0)
                 argv[0][0] = '@';
-        …
+        ...
     }
 ```
 
@@ -190,4 +190,4 @@ few additional notes for supporting these setups:
   program consult this blog story: [Socket
   Activation](http://0pointer.de/blog/projects/socket-activation.html)
 
-* Consider having a look at the [initrd Interface of systemd](http://www.freedesktop.org/wiki/Software/systemd/InitrdInterface)
+* Consider having a look at the [initrd Interface of systemd](https://systemd.io/INITRD_INTERFACE/).
index 05f93305613945c9b0af7cc93b5805f738d086cd..0b34e5325fc87e6de8f10ceeb07e81a0e0f1b6ce 100644 (file)
@@ -1,9 +1,9 @@
 ---
-title: Reporting of security vulnerabilities
+title: Reporting of Security Vulnerabilities
 category: Contributing
 layout: default
 ---
 
-# Reporting of security vulnerabilities
+# 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.
index 4d58d0f2b2b3809d5b9e0401cbb4c32e3be51e01..2622682bd9de1e0330a0293b3c2ea036e3ee5951 100644 (file)
@@ -1,10 +1,10 @@
 ---
-title: Testing systemd using sanitizers
+title: Testing systemd Using Sanitizers
 category: Contributing
 layout: default
 ---
 
-# Testing systemd using sanitizers
+# Testing systemd Using Sanitizers
 
 To catch the *nastier* kind of bugs, you can run your code with [Address Sanitizer](https://clang.llvm.org/docs/AddressSanitizer.html)
 and [Undefined Behavior Sanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html).
index e9122d5b2b6bfc67853c32c1236c009c8684c1b2..271d8ab1e3d6d23670bac8a736f987e912b17c42 100644 (file)
@@ -1,10 +1,10 @@
 ---
-title: What settings are currently available for transient units?
+title: What Settings Are Currently Available For Transient Units?
 category: Interfaces
 layout: default
 ---
 
-# What settings are currently available for transient units?
+# What Settings Are Currently Available For Transient Units?
 
 Our intention is to make all settings that are available as unit file settings
 also available for transient units, through the D-Bus API. At the moment,
@@ -192,6 +192,7 @@ All execution-related settings are available for transient units.
 ✓ PrivateUsers=
 ✓ ProtectSystem=
 ✓ ProtectHome=
+✓ ProtectClock=
 ✓ MountFlags=
 ✓ MountAPIVFS=
 ✓ Personality=
index e70c23a362d7b6c49140e8650f5a3f68c4f61f48..255cc713226968cbf0c23a3227c3c674cdc81a1e 100644 (file)
@@ -1,10 +1,10 @@
 ---
-title: Users, Groups, UIDs and GIDs on `systemd` Systems
+title: Users, Groups, UIDs and GIDs on systemd Systems
 category: Concepts
 layout: default
 ---
 
-# Users, Groups, UIDs and GIDs on `systemd` Systems
+# Users, Groups, UIDs and GIDs on systemd Systems
 
 Here's a summary of the requirements `systemd` (and Linux) make on UID/GID
 assignments and their ranges.
@@ -96,7 +96,15 @@ but downstreams are strongly advised against doing that.)
 
 `systemd` defines a number of special UID ranges:
 
-1. 61184…65519 → UIDs for dynamic users are allocated from this range (see the
+1. 60001…60513 → UIDs for home directories managed by
+   [`systemd-homed.service(8)`](https://www.freedesktop.org/software/systemd/man/systemd-homed.service.html). UIDs
+   from this range are automatically assigned to any home directory discovered,
+   and persisted locally on first login. On different systems the same user
+   might get different UIDs assigned in case of conflict, though it is
+   attempted to make UID assignments stable, by deriving them from a hash of
+   the user name.
+
+2. 61184…65519 → UIDs for dynamic users are allocated from this range (see the
    `DynamicUser=` documentation in
    [`systemd.exec(5)`](https://www.freedesktop.org/software/systemd/man/systemd.exec.html)). This
    range has been chosen so that it is below the 16bit boundary (i.e. below
@@ -111,7 +119,7 @@ but downstreams are strongly advised against doing that.)
    user record resolving works correctly without those users being in
    `/etc/passwd`.
 
-2. 524288…1879048191 → UID range for `systemd-nspawn`'s automatic allocation of
+3. 524288…1879048191 → UID range for `systemd-nspawn`'s automatic allocation of
    per-container UID ranges. When the `--private-users=pick` switch is used (or
    `-U`) then it will automatically find a so far unused 16bit subrange of this
    range and assign it to the container. The range is picked so that the upper
@@ -232,7 +240,8 @@ 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…61183 | Unused                |               |                               |
+|           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` |
diff --git a/docs/USER_GROUP_API.md b/docs/USER_GROUP_API.md
new file mode 100644 (file)
index 0000000..21d498f
--- /dev/null
@@ -0,0 +1,267 @@
+---
+title: User/Group Record Lookup API via Varlink
+category: Interfaces
+layout: default
+---
+
+# User/Group Record Lookup API via Varlink
+
+JSON User/Group Records (as described in the [JSON User
+Records](https://systemd.io/USER_RECORD) and [JSON Group
+Records](https://systemd.io/GROUP_RECORD) documents) that are defined on the
+local system may be queried with a [Varlink](https://varlink.org/) API. This
+API takes both the role of what
+[`getpwnam(3)`](http://man7.org/linux/man-pages/man3/getpwnam.3.html) and
+related calls are for `struct passwd`, as well as the interfaces modules
+implementing the [glibc Name Service Switch
+(NSS)](https://www.gnu.org/software/libc/manual/html_node/Name-Service-Switch.html)
+expose. Or in other words, it both allows applications to efficiently query
+user/group records from local services, and allows local subsystems to provide
+user/group records efficiently to local applications.
+
+This simple API only exposes only three method calls, and requires only a small
+subset of the Varlink functionality.
+
+## Why Varlink?
+
+The API described in this document is based on a simple subset of the
+mechanisms described by [Varlink](https://varlink.org/). The choice of
+preferring Varlink over D-Bus and other IPCs in this context was made for three
+reasons:
+
+1. User/Group record resolution should work during early boot and late shutdown
+   without special handling. This is very hard to do with D-Bus, as the broker
+   service for D-Bus generally runs as regular system daemon and is hence only
+   available at the latest boot stage.
+
+2. The JSON user/group records are native JSON data, hence picking an IPC
+   system that natively operates with JSON data is natural and clean.
+
+3. IPC systems such as D-Bus do not provide flow control and are thus unusable
+   for streaming data. They are useful to pass around short control messages,
+   but as soon as potentially many and large objects shall be transferred,
+   D-Bus is not suitable, as any such streaming of messages would be considered
+   flooding in D-Bus' logic, and thus possibly result in termination of
+   communication. Since the APIs defined in this document need to support
+   enumerating potentially large numbers of users and groups, D-Bus is simply
+   not an appropriate option.
+
+## Concepts
+
+Each subsystem that needs to define users and groups on the local system is
+supposed to implement this API, and offer its interfaces on a Varlink
+`AF_UNIX`/`SOCK_STREAM` file system socket bound into the
+`/run/systemd/userdb/` directory. When a client wants to look up a user or
+group record, it contacts all sockets bound in this directory in parallel, and
+enqueues the same query to each. The first positive reply is then returned to
+the application, or if all fail the last seen error is returned
+instead. (Alternatively a special Varlink service is available,
+`io.systemd.Multiplexer` which acts as frontend and will do the parallel
+queries on behalf of the client, drastically simplifying client
+development. This service is not available during earliest boot and final
+shutdown phases.)
+
+Unlike with glibc NSS there's no order or programmatic expression language
+defined in which queries are issued to the various services. Instead, all
+queries are always enqueued in parallel to all defined services, in order to
+make look-ups efficient, and the simple rule of "first successful lookup wins"
+is unconditionally followed for user and group look-ups (though not for
+membership lookups, see below).
+
+This simple scheme only works safely as long as every service providing
+user/group records carefully makes sure not to answer with conflicting
+records. This API does not define any mechanisms for dealing with user/group
+name/ID collisions during look-up nor during record registration. It assumes
+the various subsystems that want to offer user and group records to the rest of
+the system have made sufficiently sure in advance that their definitions do not
+collide with those of other services. Clients are not expected to merge
+multiple definitions for the same user or group, and will also not be able to
+detect conflicts and suppress such conflicting records.
+
+It is recommended to name the sockets in the directory in reverse domain name
+notation, but this is neither required nor enforced.
+
+## Well-Known Services
+
+Any subsystem that wants to provide user/group records can do so, simply by
+binding a socket in the aforementioned directory. By default two
+services are listening there, that have special relevance:
+
+1. `io.systemd.NameServiceSwitch` → This service makes the classic UNIX/glibc
+   NSS user/group records available as JSON User/Group records. Any such
+   records are automatically converted as needed, and possibly augmented with
+   information from the shadow databases.
+
+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
+   during earliest boot and final shutdown phases, hence for programs running
+   in that context it is preferable to implement the parallel lookup
+   themselves.
+
+Both these services are implemented by the same daemon
+`systemd-userdbd.service`.
+
+Note that these services currently implement a subset of Varlink only. For
+example, introspection is not available, and the resolver logic is not used.
+
+## Other Services
+
+The `systemd` project provides two other services implementing this
+interface. Specifically:
+
+1. `io.systemd.DynamicUser` → This service is implemented by the service
+   manager itself, and provides records for the users and groups synthesized
+   via `DynamicUser=` in unit files.
+
+2. `io.systemd.Home` → This service is implemented by `systemd-homed.service`
+   and provides records for the users and groups defined by the home
+   directories it manages.
+
+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
+enforced by systemd and defined directly in LDAP directories.
+
+## Compatibility with NSS
+
+Two-way compatibility with classic UNIX/glibc NSS user/group records is
+provided. When using the Varlink API, lookups into databases provided only via
+NSS (and not natively via Varlink) are handled by the
+`io.systemd.NameServiceSwitch` service (see above). When using the NSS API
+(i.e. `getpwnam()` and friends) the `nss-systemd` module will automatically
+synthesize NSS records for users/groups natively defined via a Varlink
+API. Special care is taken to avoid recursion between these two compatibility
+mechanisms.
+
+Subsystems that shall provide user/group records to the system may choose
+between offering them via an NSS module or via a this Varlink API, either way
+all records are accessible via both APIs, due to the bidirectional
+forwarding. It is also possible to provide the same records via both APIs
+directly, but in that case the compatibility logic must be turned off. There
+are mechanisms in place for this, please contact the systemd project for
+details, as these are currently not documented.
+
+## Caching of User Records
+
+This API defines no concepts for caching records. If caching is desired it
+should be implemented in the subsystems that provide the user records, not in
+the clients consuming them.
+
+## Method Calls
+
+```
+interface io.systemd.UserDatabase
+
+method GetUserRecord(
+        uid : ?int,
+        userName : ?string,
+        service : string
+) -> (
+        record : object,
+        incomplete : boolean
+)
+
+method GetGroupRecord(
+        gid : ?int,
+        groupName : ?string,
+        service : string
+) -> (
+        record : object,
+        incomplete : boolean
+)
+
+method GetMemberships(
+        userName : ?string,
+        groupName : ?string,
+        service : string
+) -> (
+        userName : string,
+        groupName : string
+)
+
+error NoRecordFound()
+error BadService()
+error ServiceNotAvailable()
+error ConflictingRecordFound()
+```
+
+The `GetUserRecord` method looks up or enumerates a user record. If the `uid`
+parameter is set it specifies the numeric UNIX UID to search for. If the
+`userName` parameter is set it specifies the name of the user to search
+for. Typically, only one of the two parameters are set, depending whether a
+look-up by UID or by name is desired. However, clients may also specify both
+parameters, in which case a record matching both will be returned, and if only
+one exists that matches one of the two parameters but not the other an error of
+`ConflictingRecordFound` is returned. If neither of the two parameters are set
+the whole user database is enumerated. In this case the method call needs to be
+made with `more` set, so that multiple method call replies may be generated as
+effect, each carrying one user record.
+
+The `service` parameter is mandatory and should be set to the service name
+being talked to (i.e. to the same name as the `AF_UNIX` socket path, with the
+`/run/systemd/userdb/` prefix removed). This is useful to allow implementation
+of multiple services on the same socket (which is used by
+`systemd-userdbd.service`).
+
+The method call returns one or more user records, depending which type of query is
+used (see above). The record is returned in the `record` field. The
+`incomplete` field indicates whether the record is complete. Services providing
+user record lookup should only pass the `privileged` section of user records to
+clients that either match the user the record is about or to sufficiently
+privileged clients, for all others the section must be removed so that no
+sensitive data is leaked this way. The `incomplete` parameter should indicate
+whether the record has been modified like this or not (i.e. it is `true` if a
+`privileged` section existed in the user record and was removed, and `false` if
+no `privileged` section existed or one existed but hasn't been removed).
+
+If no user record matching the specified UID or name is known the error
+`NoRecordFound` is returned (this is also returned if neither UID nor name are
+specified, and hence enumeration requested but the subsystem currently has no
+users defined).
+
+If a method call with an incorrectly set `service` field is received
+(i.e. either not set at all, or not to the service's own name) a `BadService`
+error is generated. Finally, `ServiceNotAvailable` should be returned when the
+backing subsystem is not operational for some reason and hence no information
+about existence or non-existence of a record can be returned nor any user
+record at all. (The `service` field is defined in order to allow implementation
+of daemons that provide multiple distinct user/group services over the same
+`AF_UNIX` socket: in order to correctly determine which service a client wants
+to talk to the client needs to provide the name in each request.)
+
+The `GetGroupRecord` method call works analogously but for groups.
+
+The `GetMemberships` method call may be used to inquire about group
+memberships. The `userName` and `groupName` arguments take what the name
+suggests. If one of the two is specified all matching memberships are returned,
+if neither is specified all known memberships of any user and any group are
+returned. The return value is a pair of user name and group name, where the
+user is a member of the group. If both arguments are specified the specified
+membership will be tested for, but no others, and the pair is returned if it is
+defined. Unless both arguments are specified the method call needs to be made
+with `more` set, so that multiple replies can be returned (since typically
+there are are multiple members per group and also multiple groups a user is
+member of). As with `GetUserRecord` and `GetGroupRecord` the `service`
+parameter needs to contain the name of the service being talked to, in order to
+allow implementation of multiple service within the same IPC socket. In case no
+matching membership is known `NoRecordFound` is returned. The other two errors
+are also generated in the same cases as for `GetUserRecord` and
+`GetGroupRecord`.
+
+Unlike with `GetUserRecord` and `GetGroupRecord` the lists of memberships
+returned by services are always combined. Thus unlike the other two calls a
+membership lookup query has to wait for the last simultaneous query to complete
+before the complete list is acquired.
+
+Note that only the `GetMemberships` call is authoritative about memberships of
+users in groups. i.e. it should not be considered sufficient to check the
+`memberOf` field of user records and the `members` field of group records to
+acquire the full list of memberships. The full list can only bet determined by
+`GetMemberships`, and as mentioned requires merging of these lists of all local
+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.
+
+And that's really all there is to it.
diff --git a/docs/USER_RECORD.md b/docs/USER_RECORD.md
new file mode 100644 (file)
index 0000000..3bc0879
--- /dev/null
@@ -0,0 +1,1023 @@
+---
+title: JSON User Records
+category: Interfaces
+layout: default
+---
+
+# JSON User Records
+
+systemd optionally processes user records that go beyond the classic UNIX (or
+glibc NSS) `struct passwd`. Various components of systemd are able to provide
+and consume records in a more extensible format of a dictionary of key/value
+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).
+
+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
+   settings to the activated session, including environment variables, nice
+   levels and more.
+
+3. [`systemd-logind.service`](https://www.freedesktop.org/software/systemd/man/systemd-logind.service.html)
+   processes these JSON records of users that log in, and applies various
+   resource management settings to the per-user slice units it manages. This
+   allows setting global limits on resource consumption by a specific user.
+
+4. [`nss-systemd`](https://www.freedesktop.org/software/systemd/man/nss-systemd.html)
+   is a glibc NSS module that synthesizes classic NSS records from these JSON
+   records, providing full backwards compatibility with the classic UNIX APIs
+   both for look-up and enumeration.
+
+5. The service manager (PID 1) exposes dynamic users (i.e. users synthesized as
+   effect of `DynamicUser=` in service unit files) as these advanced JSON
+   records, making them discoverable to the rest of the system.
+
+6. [`systemd-userdbd.service`](https://www.freedesktop.org/software/systemd/man/systemd-userdbd.service.html)
+   is a small service that can translate UNIX/glibc NSS records to these JSON
+   user records. It also provides a unified [Varlink](https://varlink.org/) API
+   for querying and enumerating records of this type, optionally acquiring them
+   from various other services.
+
+JSON user records may contain various fields that are not available in `struct
+passwd`, and are extensible for other applications. For example, the record may
+contain information about:
+
+1. Additional security credentials (PKCS#11 security token information,
+   biometrical authentication information, SSH public key information)
+
+2. Additional user metadata, such as a picture, email address, location string,
+   preferred language or timezone
+
+3. Resource Management settings (such as CPU/IO weights, memory and tasks
+   limits, classic UNIX resource limits or nice levels)
+
+4. Runtime parameters such as environment variables or the `nodev`, `noexec`,
+   `nosuid` flags to use for the home directory
+
+5. Information about where to mount the home directory from
+
+And various other things. The record is intended to be extensible, for example
+the following extensions are envisioned:
+
+1. Windows network credential information
+
+2. Information about default IMAP, SMTP servers to use for this user
+
+3. Parental control information to enforce on this user
+
+4. Default parameters for backup applications and similar
+
+Similar to JSON User Records there are also [JSON Group
+Records](https://systemd.io/GROUP_RECORD) that encapsulate UNIX groups.
+
+JSON User Records may be transferred or written to disk in various protocols
+and formats. To inquire about such records defined on the local system use the
+[User/Group Lookup API via Varlink](https://systemd.io/USER_GROUP_API).
+
+## Why JSON?
+
+JSON is nicely extensible and widely used. In particular it's easy to
+synthesize and process with numerous programming languages. It's particularly
+popular in the web communities, which hopefully should make it easy to link
+user credential data from the web and from local systems more closely together.
+
+## General Structure
+
+The JSON user records generated and processed by systemd follow a general
+structure, consisting of seven distinct "sections". Specifically:
+
+1. Various fields are placed at the top-level of user record (the `regular`
+   section). These are generally fields that shall apply unconditionally to the
+   user in all contexts, are portable and not security sensitive.
+
+2. A number of fields are located in the `privileged` section (a sub-object of
+   the user record). Fields contained in this object are security sensitive,
+   i.e. contain information that the user and the administrator should be able
+   to see, but other users should not. In many ways this matches the data
+   stored in `/etc/shadow` in classic Linux user accounts, i.e. includes
+   password hashes and more. Algorithmically, when a user record is passed to
+   an untrusted client, by monopolizing such sensitive records in a single
+   object field we can easily remove it from view.
+
+3. A number of fields are located in objects inside the `perMachine` section
+   (an array field of the user record). Primarily these are resource
+   management-related fields, as those tend to make sense on a specific system
+   only, e.g. limiting a user's memory use to 1G only makes sense on a specific
+   system that has more than 1G of memory. Each object inside the `perMachine`
+   array comes with a `matchMachineId` or `matchHostname` field which indicate
+   which systems to apply the listed settings to. Note that many fields
+   accepted in the `perMachine` section can also be set at the top level (the
+   `regular` section), where they define the fallback if no matching object in
+   `perMachine` is found.
+
+4. Various fields are located in the `binding` section (a sub-sub-object of the
+   user record; an intermediary object is inserted which is keyed by the
+   machine ID of the host). Fields included in this section "bind" the object
+   to a specific system. They generally include non-portable information about
+   paths or UID assignments, that are true on a specific system, but not
+   necessarily on others, and which are managed automatically by some user
+   record manager (such as `systemd-homed`). Data in this section is considered
+   part of the user record only in the local context, and is generally not
+   ported to other systems. Due to that it is not included in the reduced user
+   record the cryptographic signature defined in the `signature` section is
+   calculated on. In `systemd-homed` this section is also removed when the
+   user's record is stored in the `~/.identity` file in the home directory, so
+   that every system with access to the home directory can manage these
+   `binding` fields individually. Typically, the binding section is persisted
+   to the local disk.
+
+5. Various fields are located in the `status` section (a sub-sub-object of the
+   user record, also with an intermediary object between that is keyed by the
+   machine ID, similar to the way the `binding` section is organized). This
+   section is augmented during runtime only, and never persisted to disk. The
+   idea is that this section contains information about current runtime
+   resource usage (for example: currently used disk space of the user), that
+   changes dynamically but is otherwise immediately associated with the user
+   record and for many purposes should be considered to be part of the user
+   record.
+
+6. The `signature` section contains one or more cryptographic signatures of a
+   reduced version of the user record. This is used to ensure that only user
+   records defined by a specific source are accepted on a system, by validating
+   the signature against the set of locally accepted signature public keys. The
+   signature is calculated from the JSON user record with all sections removed,
+   except for `regular`, `privileged`, `perMachine`. Specifically, `binding`,
+   `status`, `signature` itself and `secret` are removed first and thus not
+   covered by the signature. This section is optional, and is only used when
+   cryptographic validation of user records is required (as it is by
+   `systemd-homed.service` for example).
+
+7. The `secret` section contains secret user credentials, such as password or
+   PIN information. This data is never persisted, and never returned when user
+   records are inquired by a client, privileged or not. This data should only
+   be included in a user record very briefly, for example when certain very
+   specific operations are executed. For example, in tools such as
+   `systemd-homed` this section may be included in user records, when creating
+   a new home directory, as passwords and similar credentials need to be
+   provided to encrypt the home directory with.
+
+Here's a tabular overview of the sections and their properties:
+
+| Section    | Included in Signature | Persistent | Security Sensitive | Contains Host-Specific Data |
+|------------|-----------------------|------------|--------------------|-----------------------------|
+| regular    | yes                   | yes        | no                 | no                          |
+| privileged | yes                   | yes        | yes                | no                          |
+| perMachine | yes                   | yes        | no                 | yes                         |
+| binding    | no                    | yes        | no                 | yes                         |
+| status     | no                    | no         | no                 | yes                         |
+| signature  | no                    | yes        | no                 | no                          |
+| secret     | no                    | no         | yes                | no                          |
+
+Note that services providing user records to the local system are free to
+manage only a subset of these sections and never include the others in
+them. For example, a service that has no concept of signed records (for example
+because the records it manages are inherently trusted anyway) does not have to
+bother with the `signature` section. A service that only defines records in a
+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
+the fields included therein are only useful when executing authentication
+operations natively against JSON user records.
+
+The `systemd-homed` manager uses all seven sections for various
+purposes. Inside the home directories (and if the LUKS2 backend is used, also
+in the LUKS2 header) a user record containing the `regular`, `privileged`,
+`perMachine` and `signature` sections is stored. `systemd-homed` also stores a
+version of the record on the host, with the same four sections and augmented
+with an additional, fifth `binding` section. When a local client enquires about
+a user record managed by `systemd-homed` the service will add in some
+additional information about the user and home directory in the `status`
+section — this version is only transferred via IPC and never written to
+disk. Finally the `secret` section is used during authentication operations via
+IPC to transfer the user record along with its authentication tokens in one go.
+
+## Fields in the `regular` section
+
+As mentioned, the `regular` section's fields are placed at the top level
+object. The following fields are currently defined:
+
+`userName` → The UNIX user name for this record. Takes a string with a valid
+UNIX user name. This field is the only mandatory field, all others are
+optional. Corresponds with the `pw_name` field of of `struct passwd` and the
+`sp_namp` field of `struct spwd` (i.e. the shadow user record stored in
+`/etc/shadow`).
+
+`realm` → The "realm" a user is defined in. This concept allows distinguishing
+users with the same name that originate in different organizations or
+installations. This should take a string in DNS domain syntax, but doesn't have
+to refer to an actual DNS domain (though it is recommended to use one for
+this). The idea is that the user `lpoetter` in the `redhat.com` realm might be
+distinct from the same user in the `poettering.hq` realm. User records for the
+same user name that have different realm fields are considered referring to
+different users. When updating a user record it is required that any new
+version has to match in both `userName` and `realm` field. This field is
+optional, when unset the user should not be considered part of any realm. A
+user record with a realm set is never compatible (for the purpose of updates,
+see above) with a user record without one set, even if the `userName` field matches.
+
+`realName` → The real name of the user, a string. This should contain the user's
+real ("human") name, and corresponds loosely to the GECOS field of classic UNIX
+user records. When converting a `struct passwd` to a JSON user record this
+field is initialized from GECOS (i.e. the `pw_gecos` field), and vice versa
+when converting back. That said, unlike GECOS this field is supposed to contain
+only the real name and no other information.
+
+`emailAddress` → The email address of the user, formatted as
+string. [`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
+initializes the `$EMAIL` environment variable from this value for all login
+sessions.
+
+`iconName` → The name of an icon picked by the user, for example for the
+purpose of an avatar. This must be a string, and should follow the semantics
+defined in the [Icon Naming
+Specification](https://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html).
+
+`location` → A free-form location string describing the location of the user,
+if that is applicable. It's probably wise to use a location string processable
+by geo-location subsystems, but this is not enforced nor required. Example:
+`Berlin, Germany` or `Basement, Room 3a`.
+
+`disposition` → A string, one of `intrinsic`, `system`, `dynamic`, `regular`,
+`container`, `reserved`. If specified clarifies the disposition of the user,
+i.e. the context it is defined in. For regular, "human" users this should be
+`regular`, for system users (i.e. users that system services run under, and
+similar) this should be `system`. The `intrinsic` disposition should be used
+only for the two users that have special meaning to the OS kernel itself,
+i.e. the `root` and `nobody` users. The `container` string should be used for
+users that are used by an OS container, and hence will show up in `ps` listings
+and such, but are only defined in container context. Finally `reserved` should
+be used for any users outside of these use-cases. Note that this property is
+entirely optional and applications are assumed to be able to derive the
+disposition of a user automatically from a record even in absence of this
+field, based on other fields, for example the numeric UID. By setting this
+field explicitly applications can override this default determination.
+
+`lastChangeUSec` → An unsigned 64bit integer value, referring to a timestamp in µs
+since the epoch 1970, indicating when the user record (specifically, any of the
+`regular`, `privileged`, `perMachine` sections) was last changed. This field is
+used when comparing two records of the same user to identify the newer one, and
+is used for example for automatic updating of user records, where appropriate.
+
+`lastPasswordChangeUSec` → Similar, also an unsigned 64bit integer value,
+indicating the point in time the password (or any authentication token) of the
+user was last changed. This corresponds to the `sp_lstchg` field of `struct
+spwd`, i.e. the matching field in the user shadow database `/etc/shadow`,
+though provides finer resolution.
+
+`shell` → A string, referring to the shell binary to use for terminal logins of
+this user. This corresponds with the `pw_shell` field of `struct passwd`, and
+should contain an absolute file system path. For system users not suitable for
+terminal log-in this field should not be set.
+
+`umask` → The `umask` to set for the user's login sessions. Takes an
+integer. Note that usually on UNIX the umask is noted in octal, but JSON's
+integers are generally written in decimal, hence in this context we denote it
+umask in decimal too. The specified value should be in the valid range for
+umasks, i.e. 0000…0777 (in octal as typical in UNIX), or 0…511 (in decimal, how
+it actually appears in the JSON record). This `umask` is automatically set by
+[`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
+for all login sessions of the user.
+
+`environment` → An array of strings, each containing an environment variable
+and its value to set for the user's login session, in a format compatible with
+[`putenv()`](http://man7.org/linux/man-pages/man3/putenv.3.html). Any
+environment variable listed here is automatically set by
+[`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
+for all login sessions of the user.
+
+`timeZone` → A string indicating a preferred timezone to use for the user. When
+logging in
+[`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
+will automatically initialize the `$TZ` environment variable from this
+string. The string should be a `tzdata` compatible location string, for
+example: `Europe/Berlin`.
+
+`preferredLanguage` → A string indicating the preferred language/locale for the
+user. When logging in
+[`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
+will automatically initialize the `$LANG` environment variable from this
+string. The string hence should be in a format compatible with this environment
+variable, for example: `de_DE.UTF8`.
+
+`niceLevel` → An integer value in the range -20…19. When logging in
+[`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
+will automatically initialize the login process' nice level to this value with,
+which is then inherited by all the user's processes, see
+[`setpriority()`](http://man7.org/linux/man-pages/man2/setpriority.2.html) for
+more information.
+
+`resourceLimits` → An object, where each key refers to a Linux resource limit
+(such as `RLIMIT_NOFILE` and similar). Their values should be an object with
+two keys `cur` and `max` for the soft and hard resource limit. When logging in
+[`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
+will automatically initialize the login process' resource limits to these
+values, which is then inherited by all the user's processes, see
+[`setrlimit()`](http://man7.org/linux/man-pages/man2/setrlimit.2.html) for more
+information.
+
+`locked` → A boolean value. If true the user account is locked, the user may
+not log in. If this field is missing it should be assumed to be false,
+i.e. logins are permitted. This field corresponds to the `sp_expire` field of
+`struct spwd` (i.e. the `/etc/shadow` data for a user) being set to zero or
+one.
+
+`notBeforeUSec` → An unsigned 64bit integer value, indicating a time in µs since
+the UNIX epoch (1970) before which the record should be considered invalid for
+the purpose of logging in.
+
+`notAfterUSec` → Similar, but indicates the point in time *after* which logins
+shall not be permitted anymore. This corresponds to the `sp_expire` field of
+`struct spwd`, when it is set to a value larger than one, but provides finer
+granularity.
+
+`storage` → A string, one of `classic`, `luks`, `directory`, `subvolume`,
+`fscrypt`, `cifs`. Indicates the storage mechanism for the user's home
+directory. If `classic` the home directory is a plain directory as in classic
+UNIX. When `directory`, the home directory is a regular directory, but the
+`~/.identity` file in it contains the user's user record, so that the directory
+is self-contained. Similar, `subvolume` is a `btrfs` subvolume that also
+contains a `~/.identity` user record; `fscrypt` is an `fscrypt`-encrypted
+directory, also containing the `~/.identity` user record; `luks` is a per-user
+LUKS volume that is mounted as home directory, and `cifs` a home directory
+mounted from a Windows File Share. The five latter types are primarily used by
+`systemd-homed` when managing home directories, but may be used if other
+managers are used too. If this is not set `classic` is the implied default.
+
+`diskSize` → An unsigned 64bit integer, indicating the intended home directory
+disk space in bytes to assign to the user. Depending on the selected storage
+type this might be implement differently: for `luks` this is the intended size
+of the file system and LUKS volume, while for the others this likely translates
+to classic file system quota settings.
+
+`diskSizeRelative` → Similar to `diskSize` but takes a relative value, but
+specifies a fraction of the available disk space on the selected storage medium
+to assign to the user. This unsigned integer value is normalized to 2^32 =
+100%.
+
+`skeletonDirectory` → Takes a string with the absolute path to the skeleton
+directory to populate a new home directory from. This is only used when a home
+directory is first created, and defaults to `/etc/skel` if not defined.
+
+`accessMode` → Takes an unsigned integer in the range 0…511 indicating the UNIX
+access mask for the home directory when it is first created.
+
+`tasksMax` → Takes an unsigned 64bit integer indicating the maximum number of
+tasks the user may start in parallel during system runtime. This value is
+enforced on all tasks (i.e. processes and threads) the user starts or that are
+forked off these processes regardless if the change user identity (for example
+by setuid binaries/`su`/`sudo` and
+similar). [`systemd-logind.service`](https://www.freedesktop.org/software/systemd/man/systemd-logind.service.html)
+enforces this by setting the `TasksMax` slice property for the user's slice
+`user-$UID.slice`.
+
+`memoryHigh`/`memoryMax` → These take unsigned 64bit integers indicating upper
+memory limits for all processes of the user (plus all processes forked off them
+that might have changed user identity), in bytes. Enforced by
+[`systemd-logind.service`](https://www.freedesktop.org/software/systemd/man/systemd-logind.service.html),
+similar to `tasksMax`.
+
+`cpuWeight`/`ioWeight` → These take unsigned integers in the range 1…10000
+(defaults to 100) and configure the CPU and IO scheduling weights for the
+user's processes as a whole. Also enforced by
+[`systemd-logind.service`](https://www.freedesktop.org/software/systemd/man/systemd-logind.service.html),
+similar to `tasksMax`, `memoryHigh` and `memoryMax`.
+
+`mountNoDevices`/`mountNoSuid`/`mountNoExecute` → Three booleans that control
+the `nodev`, `nosuid`, `noexec` mount flags of the user's home
+directories. Note that these booleans are only honored if the home directory
+is managed by a subsystem such as `systemd-homed.service` that automatically
+mounts home directories on login.
+
+`cifsDomain` → A string indicating the Windows File Sharing domain (CIFS) to
+use. This is generally useful, but particularly when `cifs` is used as storage
+mechanism for the user's home directory, see above.
+
+`cifsUserName` → A string indicating the Windows File Sharing user name (CIFS)
+to associate this user record with. This is generally useful, but particularly
+useful when `cifs` is used as storage mechanism for the user's home directory,
+see above.
+
+`cifsService` → A string indicating the Windows File Share service (CIFS) to
+mount as home directory of the user on login.
+
+`imagePath` → A string with an absolute file system path to the file, directory
+or block device to use for storage backing the home directory. If the `luks`
+storage is used this refers to the loopback file or block device node to store
+the LUKS volume on. For `fscrypt`, `directory`, `subvolume` this refers to the
+directory to bind mount as home directory on login. Not defined for `classic`
+or `cifs`.
+
+`homeDirectory` → A string with an absolute file system path to the home
+directory. This is where the image indicated in `imagePath` is mounted to on
+login and thus indicates the application facing home directory while the home
+directory is active, and is what the user's `$HOME` environment variable is set
+to during log-in. It corresponds to the `pw_dir` field of `struct passwd`.
+
+`uid` → An unsigned integer in the range 0…4294967295: the numeric UNIX user ID (UID) to
+use for the user.  This corresponds to the `pw_uid` field of `struct passwd`.
+
+`gid` → An unsigned integer in the range 0…4294967295: the numeric UNIX group
+ID (GID) to use for the user. This corresponds to the `pw_gid` field of
+`struct passwd`.
+
+`memberOf` → An array of strings, each indicating a UNIX group this user shall
+be a member of. The listed strings must be valid group names, but it is not
+required that all groups listed exist in all contexts: any entry for which no
+group exists should be silently ignored.
+
+`fileSystemType` → A string, one of `ext4`, `xfs`, `btrfs` (possibly others) to
+use as file system for the user's home directory. This is primarily relevant
+when the storage mechanism used is `luks` as a file system to use inside the
+LUKS container must be selected.
+
+`partitionUuid` → A string containing a lower-case, text-formatted UUID, referencing
+the GPT partition UUID the home directory is located in. This is primarily
+relevant when the storage mechanism used is `luks`.
+
+`luksUuid` → A string containing a lower-case, text-formatted UUID, referencing
+the LUKS volume UUID the home directory is located in. This is primarily
+relevant when the storage mechanism used is `luks`.
+
+`fileSystemUuid` → A string containing a lower-case, text-formatted UUID,
+referencing the file system UUID the home directory is located in. This is
+primarily relevant when the storage mechanism used is `luks`.
+
+`luksDiscard` → A boolean. If true and `luks` storage is used controls whether
+the loopback block devices, LUKS and the file system on top shall be used in
+`discard` mode, i.e. erased sectors should always be returned to the underlying
+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.
+
+`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.
+
+`luksVolumeKeySize` → An unsigned integer, indicating the volume key length in
+bytes to use for the LUKS storage mechanism.
+
+`luksPbkdfHashAlgorithm` → A string, selecting the hash algorithm to use for
+the PBKDF operation for the LUKS storage mechanism.
+
+`luksPbkdfType` → A string, indicating the PBKDF type to use for the LUKS storage mechanism.
+
+`luksPbkdfTimeCostUSec` → An unsigned 64bit integer, indicating the intended
+time cost for the PBKDF operation, when the LUKS storage mechanism is used, in
+µs.
+
+`luksPbkdfMemoryCost` → An unsigned 64bit integer, indicating the intended
+memory cost for the PBKDF operation, when LUKS storage is used, in bytes.
+
+`luksPbkdfParallelThreads` → An unsigned 64bit integer, indicating the intended
+required parallel threads for the PBKDF operation, when LUKS storage is used.
+
+`service` → A string declaring the service that defines or manages this user
+record. It is recommended to use reverse domain name notation for this. For
+example, if `systemd-homed` manages a user a string of `io.systemd.Home` is
+used for this.
+
+`rateLimitIntervalUSec` → An unsigned 64bit integer that configures the
+authentication rate limiting enforced on the user account. This specifies a
+timer interval (in µs) within which to count authentication attempts. When the
+counter goes above the value configured n `rateLimitIntervalBurst` log-ins are
+temporarily refused until the interval passes.
+
+`rateLimitIntervalBurst` → An unsigned 64bit integer, closely related to
+`rateLimitIntervalUSec`, that puts a limit on authentication attempts within
+the configured time interval.
+
+`enforcePasswordPolicy` → A boolean. Configures whether to enforce the system's
+password policy when creating the home directory for the user or changing the
+user's password. By default the policy is enforced, but if this field is false
+it is bypassed.
+
+`autoLogin` → A boolean. If true the user record is marked as suitable for
+auto-login. Systems are supposed to automatically log in a user marked this way
+during boot, if there's exactly one user on it defined this way.
+
+`stopDelayUSec` → An unsigned 64bit integer, indicating the time in µs the
+per-user service manager is kept around after the user fully logged out.  This
+value is honored by
+[`systemd-logind.service`](https://www.freedesktop.org/software/systemd/man/systemd-logind.service.html). If
+set to zero the per-user service manager is immediately terminated when the
+user logs out, and longer values optimize high-frequency log-ins as the
+necessary work to set up and tear down a log-in is reduced if the service
+manager stays running.
+
+`killProcesses` → A boolean. If true all processes of the user are
+automatically killed when the user logs out. This is enforced by
+[`systemd-logind.service`](https://www.freedesktop.org/software/systemd/man/systemd-logind.service.html). If
+false any processes left around when the user logs out are left running.
+
+`passwordChangeMinUSec`/`passwordChangeMaxUSec` → An unsigned 64bit integer,
+encoding how much time has to pass at least/at most between password changes of
+the user. This corresponds with the `sp_min` and `sp_max` fields of `struct
+spwd` (i.e. the `/etc/shadow` entries of the user), but offers finer
+granularity.
+
+`passwordChangeWarnUSec` → An unsigned 64bit integer, encoding how much time to
+warn the user before their password expires, in µs. This corresponds with the
+`sp_warn` field of `struct spwd`.
+
+`passwordChangeInactiveUSec` → An unsigned 64bit integer, encoding how much
+time has to pass after the password expired that the account is
+deactivated. This corresponds with the `sp_inact` field of `struct spwd`.
+
+`passwordChangeNow` → A boolean. If true the user has to change their password
+on next login. This corresponds with the `sp_lstchg` field of `struct spwd`
+being set to zero.
+
+`pkcs11TokenUri` → An array of strings, each with an RFC 7512 compliant PKCS#11
+URI referring to security token (or smart card) of some form, that shall be
+associated with the user and may be used for authentication. The URI is used to
+search for an X.509 certificate and associated private key that may be used to
+decrypt an encrypted secret key that is used to unlock the user's account (see
+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 the `privileged` section
+of the user record, see below.
+
+`perMachine` → An array of objects, which contain the `perMachine` section of
+the user record, and thus fields to apply on specific systems only, see below.
+
+`binding` → An object, keyed by machine IDs formatted as strings, pointing
+to objects that contain the `binding` section of the user record,
+i.e. additional fields that bind the user record to a specific machine, see
+below.
+
+`status` → An object, keyed by machine IDs formatted as strings, pointing to
+objects that contain the `status` section of the user record, i.e. additional
+runtime fields that expose the current status of the user record on a specific
+system, see below.
+
+`signature` → An array of objects, which contain cryptographic signatures of
+the user record, i.e. the fields of the `signature` section of the user record,
+see below.
+
+`secret` → An object, which contains the fields of the `secret` section of the
+user record, see below.
+
+## Fields in the `privileged` section
+
+As mentioned, the `privileged` section is encoded in a sub-object of the user
+record top-level object, in the `privileged` field. Any data included in this
+object shall only be visible to the administrator and the user themselves, and
+be suppressed implicitly when other users get access to a user record. It thus
+takes the role of the `/etc/shadow` records for each user, which has similarly
+restrictive access semantics. The following fields are currently defined:
+
+`passwordHint` → A user-selected password hint in free-form text. This should
+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
+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`
+field of `struct passwd`).
+
+`sshAuthorizedKeys` → An array of strings, each listing an SSH public key that
+is authorized to access the account. The strings should follow the same format
+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
+`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
+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
+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.
+
+## Fields in the `perMachine` section
+
+As mentioned, the `perMachine` section contains settings that shall apply to
+specific systems only. This is primarily interesting for resource management
+properties as they tend to require a per-system focus, however they may be used
+for other purposes too.
+
+The `perMachine` field in the top-level object is an array of objects. When
+processing the user record first the various fields on the top-level object
+should be used. Then this array should be iterated in order, and the various
+settings be applied that match either the indicated machine ID or host
+name. There may be multiple array entries that match a specific system, in
+which case all the object's setting should be applied. If the same option is
+set in the top-level object as in a per-machine object the latter wins and
+entirely undoes the setting in the top-level object (i.e. no merging of
+properties that are arrays themselves is done). If the same option is set in
+multiple per-machine objects the one specified later in the array wins (and
+here too no merging of individual fields is done, the later field always wins
+in full).
+
+The following fields are defined in this section:
+
+`matchMachineId` → An array of strings with each a formatted 128bit ID in
+hex. If any of the specified IDs match the system's local machine ID
+(i.e. matches `/etc/machine-id`) the fields in this object are honored.
+
+`matchHostname` → An array of string with a each a valid hostname. If any of
+the specified hostnames match the system's local hostname, the fields in this
+object are honored. If both `matchHostname` and `matchMachineId` are used
+within the same array entry, the object is honored when either match succeeds,
+i.e. the two match types are combined in OR, not in AND.
+
+These two are the only two fields specific to this section. All other fields
+that may be used in this section are identical to the equally named ones in the
+`regular` section (i.e. at the top-level object). Specifically, these are:
+
+`iconName`, `location`, `shell`, `umask`, `environment`, `timeZone`,
+`preferredLanguage`, `niceLevel`, `resourceLimits`, `locked`, `notBeforeUSec`,
+`notAfterUSec`, `storage`, `diskSize`, `diskSizeRelative`, `skeletonDirectory`,
+`accessMode`, `tasksMax`, `memoryHigh`, `memoryMax`, `cpuWeight`, `ioWeight`,
+`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`.
+
+## Fields in the `binding` section
+
+As mentioned, the `binding` section contains additional fields about the user
+record, that bind it to the local system. These fields are generally used by a
+local user manager (such as `systemd-homed.service`) to add in fields that make
+sense in a local context but not necessarily in a global one. For example, a
+user record that contains no `uid` field in the regular section is likely
+extended with one in the `binding` section to assign a local UID if no global
+UID is defined.
+
+All fields in the `binding` section only make sense in a local context and are
+suppressed when the user record is ported between systems. The `binding` section
+is generally persisted on the system but not in the home directories themselves
+and the home directory is supposed to be fully portable and thus not contain
+the information that `binding` is supposed to contain that binds the portable
+record to a specific system.
+
+The `binding` sub-object on the top-level user record object is keyed by the
+machine ID the binding is intended for, which point to an object with the
+fields of the bindings. These fields generally match fields that may also be
+defined in the `regular` and `perMachine` sections, however override
+both. Usually, the `binding` value should not contain settings different from
+those set via `regular` or `perMachine`, however this might happen if some
+settings are not supported locally (think: `fscrypt` is recorded as intended
+storage mechanism in the `regular` section, but the local kernel does not
+support `fscrypt`, hence `directory` was chosen as implicit fallback), or have
+been changed in the `regular` section through updates (e.g. a home directory
+was created with `luks` as storage mechanism but later the user record was
+updated to prefer `subvolume`, which however doesn't change the actual storage
+used already which is pinned in the `binding` section).
+
+The following fields are defined in the `binding` section. They all have an
+identical format and override their equally named counterparts in the `regular`
+and `perMachine` sections:
+
+`imagePath`, `homeDirectory`, `partitionUuid`, `luksUuid`, `fileSystemUuid`,
+`uid`, `gid`, `storage`, `fileSystemType`, `luksCipher`, `luksCipherMode`,
+`luksVolumeKeySize`.
+
+## Fields in the `status` section
+
+As mentioned, the `status` section contains additional fields about the user
+record that are exclusively acquired during runtime, and that expose runtime
+metrics of the user and similar metadata that shall not be persisted but are
+only acquired "on-the-fly" when requested.
+
+This section is arranged similarly to the `binding` section: the `status`
+sub-object of the top-level user record object is keyed by the machine ID,
+which points to the object with the fields defined here. The following fields
+are defined:
+
+`diskUsage` → An unsigned 64bit integer. The currently used disk space of the
+home directory in bytes. This value might be determined in different ways,
+depending on the selected storage mechanism. For LUKS storage this is the file
+size of the loopback file or block device size. For the
+directory/subvolume/fscrypt storage this is the current disk space used as
+reported by the file system quota subsystem.
+
+`diskFree` → An unsigned 64bit integer, denoting the number of "free" bytes in
+the disk space allotment, i.e. usually the difference between the disk size as
+reported by `diskSize` and the used already as reported in `diskFree`, but
+possibly skewed by metadata sizes, disk compression and similar.
+
+`diskSize` → An unsigned 64bit integer, denoting the disk space currently
+allotted to the user, in bytes. Depending on the storage mechanism this can mean
+different things (see above). In contrast to the top-level field of the same
+(or the one in the `perMachine` section), this field reports the current size
+allotted to the user, not the intended one. The values may differ when user
+records are updated without the home directory being re-sized.
+
+`diskCeiling`/`diskFloor` → Unsigned 64bit integers indicating upper and lower
+bounds when changing the `diskSize` value, in bytes. These values are typically
+derived from the underlying data storage, and indicate in which range the home
+directory may be re-sized in, i.e. in which sensible range the `diskSize` value
+should be kept.
+
+`state` → A string indicating the current state of the home directory. The
+precise set of values exposed here are up to the service managing the home
+directory to define (i.e. are up to the service identified with the `service`
+field below). However, it is recommended to stick to a basic vocabulary here:
+`inactive` for a home directory currently not mounted, `absent` for a home
+directory that cannot be mounted currently because it does not exist on the
+local system, `active` for a home directory that is currently mounted and
+accessible.
+
+`service` → A string identifying the service that manages this user record. For
+example `systemd-homed.service` sets this to `io.systemd.Home` to all user
+records it manages. This is particularly relevant to define clearly the context
+in which `state` lives, see above. Note that this field also exists on the
+top-level object (i.e. in the `regular` section), which it overrides. The
+`regular` field should be used if conceptually the user record can only be
+managed by the specified service, and this `status` field if it can
+conceptually be managed by different managers, but currently is managed by the
+specified one.
+
+`signedLocally` → A boolean. If true indicates that the user record is signed
+by a public key for which the private key is available locally. This means that
+the user record may be modified locally as it can be re-signed with the private
+key. If false indicates that the user record is signed by a public key
+recognized by the local manager but whose private key is not available
+locally. This means the user record cannot be modified locally as it couldn't
+be signed afterwards.
+
+`goodAuthenticationCounter` → An unsigned 64bit integer. This counter is
+increased by one on every successful authentication attempt, i.e. an
+authentication attempt where a security token of some form was presented and it
+was correct.
+
+`badAuthenticationCounter` → An unsigned 64bit integer. This counter is
+increased by one on every unsuccessfully authentication attempt, i.e. an
+authentication attempt where a security token of some form was presented and it
+was incorrect.
+
+`lastGoodAuthenticationUSec` → An unsigned 64bit integer, indicating the time
+of the last successful authentication attempt in µs since the UNIX epoch (1970).
+
+`lastBadAuthenticationUSec` → Similar, but the timestamp of the last
+unsuccessfully authentication attempt.
+
+`rateLimitBeginUSec` → An unsigned 64bit integer: the µs timestamp since the
+UNIX epoch (1970) where the most recent rate limiting interval has been
+started, as configured with `rateLimitIntervalUSec`.
+
+`rateLimitCount` → An unsigned 64bit integer, counting the authentication
+attempts in the current rate limiting interval, see above. If this counter
+grows beyond the value configured in `rateLimitBurst` authentication attempts
+are temporarily refused.
+
+`removable` → A boolean value. If true the manager of this user record
+determined the home directory being on removable media. If false it was
+determined the home directory is in internal built-in media. (This is used by
+`systemd-logind.service` to automatically pick the right default value for
+`stopDelayUSec` if the field is not explicitly specified: for home directories
+on removable media the delay is selected very low to minimize the chance the
+home directory remains in unclean state if the storage device is removed from
+the system by the user).
+
+## Fields in the `signature` section
+
+As mentioned, the `signature` section of the user record may contain one or
+more cryptographic signatures of the user record. Like all others, this section
+is optional, and only used when cryptographic validation of user records shall
+be used. Specifically, all user records managed by `systemd-homed.service` will
+carry such signatures and the service refuses managing user records that come
+without signature or with signatures not recognized by any locally defined
+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
+private key was used to make the signature, in PEM format. Currently only
+signatures with Ed25519 keys are defined.
+
+Before signing the user record should be brought into "normalized" form,
+i.e. the keys in all objects should be sorted alphabetically. All redundant
+white-space and newlines should be removed and the JSON text then signed.
+
+The signatures only cover the `regular`, `perMachine` and `privileged` sections
+of the user records, all other sections (include `signature` itself), are
+removed before the signature is calculated.
+
+Rationale for signing and threat model: while a multi-user operating system
+like Linux strives for being sufficiently secure even after a user acquired a
+local login session reality tells us this is not the case. Hence it is
+essential to restrict carefully which users may gain access to a system and
+which ones shall not. A minimal level of trust must be established between
+system, user record and the user themselves before a log-in request may be
+permitted. In particular if the home directory is provided in its own LUKS2
+encapsulated file system it is essential this trust is established before the
+user logs in (and hence the file system mounted), since file system
+implementations on Linux are well known to be relatively vulnerable to rogue
+disk images. User records and home directories in many context are expected to
+be something shareable between multiple systems, and the transfer between them
+might not happen via exclusively trusted channels. Hence it's essential that
+the user record is not manipulated between uses. Finally, resource management
+(which may be done by the various fields of the user record) is security
+sensitive, since it should forcefully lock the user into the assigned resource
+usage and not allow them to use more. The requirement of being able to trust
+the user record data combined with the potential transfer over untrusted
+channels suggest a cryptographic signature mechanism where only user records
+signed by a recognized key are permitted to log in locally.
+
+Note that other mechanisms for establishing sufficient trust exist too, and are
+perfectly valid as well. For example, systems like LDAP/ActiveDirectory
+generally insist on user record transfer from trusted servers via encrypted TLS
+channels only. Or traditional UNIX users created locally in `/etc/passwd` never
+exist outside of the local trusted system, hence transfer and trust in the
+source are not an issue. The major benefit of operating with signed user
+records is that they are self-sufficiently trusted, not relying on a secure
+channel for transfer, and thus being compatible with a more distributed model
+of home directory transfer, including on USB sticks and such.
+
+## Fields in the `secret` section
+
+As mentioned, the `secret` section of the user record should never be persisted
+nor transferred across machines. It is only defined in short-lived operations,
+for example when a user record is first created or registered, as the secret
+key data needs to be available to derive encryption keys from and similar.
+
+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.
+
+`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.
+
+## Mapping to `struct passwd` and `struct spwd`
+
+When mapping classic UNIX user records (i.e. `struct passwd` and `struct spwd`)
+to JSON user records the following mappings should be applied:
+
+| Structure       | Field       | Section      | Field                        | Condition                  |
+|-----------------|-------------|--------------|------------------------------|----------------------------|
+| `struct passwd` | `pw_name`   | `regular`    | `userName`                   |                            |
+| `struct passwd` | `pw_passwd` | `privileged` | `password`                   | (See notes below)          |
+| `struct passwd` | `pw_uid`    | `regular`    | `uid`                        |                            |
+| `struct passwd` | `pw_gid`    | `regular`    | `gid`                        |                            |
+| `struct passwd` | `pw_gecos`  | `regular`    | `realName`                   |                            |
+| `struct passwd` | `pw_dir`    | `regular`    | `homeDirectory`              |                            |
+| `struct passwd` | `pw_shell`  | `regular`    | `shell`                      |                            |
+| `struct spwd`   | `sp_namp`   | `regular`    | `userName`                   |                            |
+| `struct spwd`   | `sp_pwdp`   | `privileged` | `password`                   | (See notes below)          |
+| `struct spwd`   | `sp_lstchg` | `regular`    | `lastPasswordChangeUSec`     | (if `sp_lstchg` > 0)       |
+| `struct spwd`   | `sp_lstchg` | `regular`    | `passwordChangeNow`          | (if `sp_lstchg` == 0)      |
+| `struct spwd`   | `sp_min`    | `regular`    | `passwordChangeMinUSec`      |                            |
+| `struct spwd`   | `sp_max`    | `regular`    | `passwordChangeMaxUSec`      |                            |
+| `struct spwd`   | `sp_warn`   | `regular`    | `passwordChangeWarnUSec`     |                            |
+| `struct spwd`   | `sp_inact`  | `regular`    | `passwordChangeInactiveUSec` |                            |
+| `struct spwd`   | `sp_expire` | `regular`    | `locked`                     | (if `sp_expire` in [0, 1]) |
+| `struct spwd`   | `sp_expire` | `regular`    | `notAfterUSec`               | (if `sp_expire` > 1)       |
+
+At this time almost all Linux machines employ shadow passwords, thus the
+`pw_passwd` field in `struct passwd` is set to `"x"`, and the actual password
+is stored in the shadow entry `struct spwd`'s field `sp_pwdp`.
+
+## Extending These Records
+
+User records following this specifications are supposed to be extendable for
+various applications. In general, subsystems are free to introduce their own
+keys, as long as:
+
+* Care should be taken to place the keys in the right section, i.e. the most
+  appropriate for the data field.
+
+* Care should be taken to avoid namespace clashes. Please prefix your fields
+  with a short identifier of your project to avoid ambiguities and
+  incompatibilities.
+
+* This specification is supposed to be a living specification. If you need
+  additional fields, please consider submitting them upstream for inclusion in
+  this specification. If they are reasonably universally useful, it would be
+  best to list them here.
+
+## Examples
+
+The shortest valid user record looks like this:
+
+```json
+{
+        "userName" : "u"
+}
+```
+
+A reasonable user record for a system user might look like this:
+
+```json
+{
+        "userName" : "httpd",
+        "uid" : 473,
+        "gid" : 473,
+        "disposition" : "system",
+        "locked" : true
+}
+```
+
+A fully featured user record associated with a home directory managed by
+`systemd-homed.service` might look like this:
+
+```json
+{
+        "autoLogin" : true,
+        "binding" : {
+                "15e19cf24e004b949ddaac60c74aa165" : {
+                        "fileSystemType" : "ext4",
+                        "fileSystemUuid" : "758e88c8-5851-4a2a-b88f-e7474279c111",
+                        "gid" : 60232,
+                        "homeDirectory" : "/home/grobie",
+                        "imagePath" : "/home/grobie.home",
+                        "luksCipher" : "aes",
+                        "luksCipherMode" : "xts-plain64",
+                        "luksUuid" : "e63581ba-79fb-4226-b9de-1888393f7573",
+                        "luksVolumeKeySize" : 32,
+                        "partitionUuid" : "41f9ce04-c827-4b74-a981-c669f93eb4dc",
+                        "storage" : "luks",
+                        "uid" : 60232
+                }
+        },
+        "disposition" : "regular",
+        "enforcePasswordPolicy" : false,
+        "lastChangeUSec" : 1565950024279735,
+        "memberOf" : [
+                "wheel"
+        ],
+        "privileged" : {
+                "hashedPassword" : [
+                        "$6$WHBKvAFFT9jKPA4k$OPY4D4TczKN/jOnJzy54DDuOOagCcvxxybrwMbe1SVdm.Bbr.zOmBdATp.QrwZmvqyr8/SafbbQu.QZ2rRvDs/"
+                ]
+        },
+        "signature" : [
+                {
+                        "data" : "LU/HeVrPZSzi3MJ0PVHwD5m/xf51XDYCrSpbDRNBdtF4fDVhrN0t2I2OqH/1yXiBidXlV0ptMuQVq8KVICdEDw==",
+                        "key" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA/QT6kQWOAMhDJf56jBmszEQQpJHqDsGDMZOdiptBgRk=\n-----END PUBLIC KEY-----\n"
+                }
+        ],
+        "userName" : "grobie",
+        "status" : {
+                "15e19cf24e004b949ddaac60c74aa165" : {
+                        "goodAuthenticationCounter" : 16,
+                        "lastGoodAuthenticationUSec" : 1566309343044322,
+                        "rateLimitBeginUSec" : 1566309342340723,
+                        "rateLimitCount" : 1,
+                        "state" : "inactive",
+                        "service" : "io.systemd.Home",
+                        "diskSize" : 161118667776,
+                        "diskCeiling" : 190371729408,
+                        "diskFloor" : 5242880,
+                        "signedLocally" : true
+                }
+        }
+}
+```
+
+When `systemd-homed.service` manages a home directory it will also include a
+version of the user record in the home directory itself in the `~/.identity`
+file. This version lacks the `binding` and `status` sections which are used for
+local management of the user, but are not intended to be portable between
+systems. It would hence look like this:
+
+```json
+{
+        "autoLogin" : true,
+        "disposition" : "regular",
+        "enforcePasswordPolicy" : false,
+        "lastChangeUSec" : 1565950024279735,
+        "memberOf" : [
+                "wheel"
+        ],
+        "privileged" : {
+                "hashedPassword" : [
+                        "$6$WHBKvAFFT9jKPA4k$OPY4D4TczKN/jOnJzy54DDuOOagCcvxxybrwMbe1SVdm.Bbr.zOmBdATp.QrwZmvqyr8/SafbbQu.QZ2rRvDs/"
+                ]
+        },
+        "signature" : [
+                {
+                        "data" : "LU/HeVrPZSzi3MJ0PVHwD5m/xf51XDYCrSpbDRNBdtF4fDVhrN0t2I2OqH/1yXiBidXlV0ptMuQVq8KVICdEDw==",
+                        "key" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA/QT6kQWOAMhDJf56jBmszEQQpJHqDsGDMZOdiptBgRk=\n-----END PUBLIC KEY-----\n"
+                }
+        ],
+        "userName" : "grobie",
+}
+```
index d8c1535f28481c1a8f9add4dc76aabd4049958d9..95289732b7b350353887b77a4e977303a2aa029c 100644 (file)
@@ -1,5 +1,5 @@
 <footer class="site-footer">
-  <p>&copy; systemd, 2019</p>
+  <p>&copy; systemd, 2020</p>
 
   <p><a href="https://github.com/systemd/systemd">Website source</a></p>
 </footer>
index 0368417184916083f8f1cb212694b5e9192746ca..c853d8020ec4393ce482558b8d0877985d155562 100644 (file)
@@ -8,8 +8,6 @@ systemd provides aggressive parallelization capabilities, uses socket and D-Bus
 
 Other parts include a logging daemon, utilities to control basic system configuration like the hostname, date, locale, maintain a list of logged-in users and running containers and virtual machines, system accounts, runtime directories and settings, and daemons to manage simple network configuration, network time synchronization, log forwarding, and name resolution.
 
-See the introductory blog story and three status updates for a longer introduction. Also see the [Wikipedia article](https://en.wikipedia.org/wiki/systemd).
-
 ---
 
 {% assign by_category = site.pages | group_by:"category" %}
@@ -24,3 +22,74 @@ See the introductory blog story and three status updates for a longer introducti
   {% endif %}
 {% endfor %}
 
+## See also
+
+* [Introductory blog story](http://0pointer.de/blog/projects/systemd.html)
+* [Three](http://0pointer.de/blog/projects/systemd-update.html) [status](http://0pointer.de/blog/projects/systemd-update-2.html) [updates](http://0pointer.de/blog/projects/systemd-update-3.html)
+* The [Wikipedia article](https://en.wikipedia.org/wiki/systemd)
+
+---
+
+<pre style="color:white; background-color:black; font-size:smaller; padding:6pt 8pt">
+Welcome to <span style="color:blue">Fedora 20 (Heisenbug)</span>!
+
+[  <span style="color:green">OK</span>  ] Reached target Remote File Systems.
+[  <span style="color:green">OK</span>  ] Listening on Delayed Shutdown Socket.
+[  <span style="color:green">OK</span>  ] Listening on /dev/initctl Compatibility Named Pipe.
+[  <span style="color:green">OK</span>  ] Reached target Paths.
+[  <span style="color:green">OK</span>  ] Reached target Encrypted Volumes.
+[  <span style="color:green">OK</span>  ] Listening on Journal Socket.
+         Mounting Huge Pages File System...
+         Mounting POSIX Message Queue File System...
+         Mounting Debug File System...
+         Starting Journal Service...
+[  <span style="color:green">OK</span>  ] Started Journal Service.
+         Mounting Configuration File System...
+         Mounting FUSE Control File System...
+[  <span style="color:green">OK</span>  ] Created slice Root Slice.
+[  <span style="color:green">OK</span>  ] Created slice User and Session Slice.
+[  <span style="color:green">OK</span>  ] Created slice System Slice.
+[  <span style="color:green">OK</span>  ] Reached target Slices.
+[  <span style="color:green">OK</span>  ] Reached target Swap.
+         Mounting Temporary Directory...
+[  <span style="color:green">OK</span>  ] Reached target Local File Systems (Pre).
+         Starting Load Random Seed...
+         Starting Load/Save Random Seed...
+[  <span style="color:green">OK</span>  ] Mounted Huge Pages File System.
+[  <span style="color:green">OK</span>  ] Mounted POSIX Message Queue File System.
+[  <span style="color:green">OK</span>  ] Mounted Debug File System.
+[  <span style="color:green">OK</span>  ] Mounted Configuration File System.
+[  <span style="color:green">OK</span>  ] Mounted FUSE Control File System.
+[  <span style="color:green">OK</span>  ] Mounted Temporary Directory.
+[  <span style="color:green">OK</span>  ] Started Load Random Seed.
+[  <span style="color:green">OK</span>  ] Started Load/Save Random Seed.
+[  <span style="color:green">OK</span>  ] Reached target Local File Systems.
+         Starting Recreate Volatile Files and Directories...
+         Starting Trigger Flushing of Journal to Persistent Storage...
+[  <span style="color:green">OK</span>  ] Started Recreate Volatile Files and Directories.
+         Starting Update UTMP about System Reboot/Shutdown...
+[  <span style="color:green">OK</span>  ] Started Trigger Flushing of Journal to Persistent Storage.
+[  <span style="color:green">OK</span>  ] Started Update UTMP about System Reboot/Shutdown.
+[  <span style="color:green">OK</span>  ] Reached target System Initialization.
+[  <span style="color:green">OK</span>  ] Reached target Timers.
+[  <span style="color:green">OK</span>  ] Listening on D-Bus System Message Bus Socket.
+[  <span style="color:green">OK</span>  ] Reached target Sockets.
+[  <span style="color:green">OK</span>  ] Reached target Basic System.
+         Starting Permit User Sessions...
+         Starting D-Bus System Message Bus...
+[  <span style="color:green">OK</span>  ] Started D-Bus System Message Bus.
+         Starting Login Service...
+         Starting Cleanup of Temporary Directories...
+[  <span style="color:green">OK</span>  ] Started Permit User Sessions.
+[  <span style="color:green">OK</span>  ] Started Cleanup of Temporary Directories.
+         Starting Console Getty...
+[  <span style="color:green">OK</span>  ] Started Console Getty.
+[  <span style="color:green">OK</span>  ] Reached target Login Prompts.
+[  <span style="color:green">OK</span>  ] Started Login Service.
+[  <span style="color:green">OK</span>  ] Reached target Multi-User System.
+
+Fedora release 20 (Heisenbug)
+Kernel 3.9.2-200.fc18.x86_64 on an x86_64 (console)
+
+fedora login:
+</pre>
index 5470993e34b2a616598f598953ad2f5e48fe03fc..e7365cd1426505ee330a1f4a492b56a0660a1ad6 100644 (file)
@@ -1,7 +1,7 @@
 # This file is part of systemd.
 
 passwd:         compat mymachines systemd
-group:          compat mymachines systemd
+group:          compat [SUCCESS=merge] mymachines [SUCCESS=merge] systemd
 shadow:         compat
 
 hosts:          files mymachines resolve [!UNAVAIL=return] dns myhostname
index 747efc1fbdb69e32d50abc18bc138aab296ef570..522c51324af17a6bf3c13c5e7b9cb46671d17a28 100644 (file)
@@ -3,17 +3,21 @@
 # You really want to adjust this to your local distribution. If you use this
 # unmodified you are not building systems safely and securely.
 
-auth     sufficient pam_unix.so nullok try_first_pass
-auth     required   pam_deny.so
+auth      sufficient pam_unix.so
+-auth     sufficient pam_systemd_home.so
+auth      required   pam_deny.so
 
-account  required   pam_nologin.so
-account  sufficient pam_unix.so
-account  required   pam_permit.so
+account   required   pam_nologin.so
+-account  sufficient pam_systemd_home.so
+account   sufficient pam_unix.so
+account   required   pam_permit.so
 
-password sufficient pam_unix.so nullok sha512 shadow try_first_pass try_authtok
-password required   pam_deny.so
+-password sufficient pam_systemd_home.so
+password  sufficient pam_unix.so sha512 shadow try_first_pass try_authtok
+password  required   pam_deny.so
 
--session optional   pam_keyinit.so revoke
--session optional   pam_loginuid.so
--session optional   pam_systemd.so
-session  sufficient pam_unix.so
+-session  optional   pam_keyinit.so revoke
+-session  optional   pam_loginuid.so
+-session  optional   pam_systemd_home.so
+-session  optional   pam_systemd.so
+session   required   pam_unix.so
index 18c70e3555323a9795f8d3f4589f551228a14cbd..2cd1763a6dda8fb61568d71a94d9e8776e58790d 100644 (file)
@@ -5,11 +5,8 @@ setup:
 - sudo apt-get update -y
 - sudo apt-get build-dep -y systemd
 - sudo apt-get install -y python3-pip
-# FIXME: temporarily pin the meson version as 0.53 doesn't work with older
-# python 3.5
-# # See: https://github.com/mesonbuild/meson/issues/6427
-#
-- pip3 install meson==0.52.1 ninja
+- 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
index c1e974ad4583a8d75a5cd1b11c4a6590d0e01101..15afdaf71951b0f5830c9071b9cbea480a4e7c56 100644 (file)
@@ -414,7 +414,7 @@ OUI:000087*
  ID_OUI_FROM_DATABASE=HITACHI, LTD.
 
 OUI:000088*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:000089*
  ID_OUI_FROM_DATABASE=CAYMAN SYSTEMS INC.
@@ -819,7 +819,7 @@ OUI:00010E*
  ID_OUI_FROM_DATABASE=Bri-Link Technologies Co., Ltd
 
 OUI:00010F*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:000110*
  ID_OUI_FROM_DATABASE=Gotham Networks
@@ -3462,7 +3462,7 @@ OUI:00047F*
  ID_OUI_FROM_DATABASE=Chr. Mayr GmbH & Co. KG
 
 OUI:000480*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:000481*
  ID_OUI_FROM_DATABASE=Econolite Control Products, Inc.
@@ -3936,7 +3936,7 @@ OUI:00051D*
  ID_OUI_FROM_DATABASE=Airocon, Inc.
 
 OUI:00051E*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:00051F*
  ID_OUI_FROM_DATABASE=Taijin Media Co., Ltd.
@@ -3999,7 +3999,7 @@ OUI:000532*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
 OUI:000533*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:000534*
  ID_OUI_FROM_DATABASE=Northstar Engineering Ltd.
@@ -7272,7 +7272,7 @@ OUI:000990*
  ID_OUI_FROM_DATABASE=ACKSYS Communications & systems
 
 OUI:000991*
- ID_OUI_FROM_DATABASE=GE Fanuc Automation Manufacturing, Inc.
+ ID_OUI_FROM_DATABASE=Intelligent Platforms, LLC.
 
 OUI:000992*
  ID_OUI_FROM_DATABASE=InterEpoch Technology,INC.
@@ -7641,7 +7641,7 @@ OUI:000A0C*
  ID_OUI_FROM_DATABASE=Scientific Research Corporation
 
 OUI:000A0D*
- ID_OUI_FROM_DATABASE=FCI Deutschland GmbH
+ ID_OUI_FROM_DATABASE=Amphenol
 
 OUI:000A0E*
  ID_OUI_FROM_DATABASE=Invivo Research Inc.
@@ -9288,7 +9288,7 @@ OUI:000C31*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
 OUI:000C32*
- ID_OUI_FROM_DATABASE=Avionic Design Development GmbH
+ ID_OUI_FROM_DATABASE=Avionic Design GmbH
 
 OUI:000C33*
  ID_OUI_FROM_DATABASE=Compucase Enterprise Co. Ltd.
@@ -9795,7 +9795,7 @@ OUI:000CDA*
  ID_OUI_FROM_DATABASE=FreeHand Systems, Inc.
 
 OUI:000CDB*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:000CDC*
  ID_OUI_FROM_DATABASE=BECS Technology, Inc
@@ -11403,7 +11403,7 @@ OUI:000EF2*
  ID_OUI_FROM_DATABASE=Infinico Corporation
 
 OUI:000EF3*
- ID_OUI_FROM_DATABASE=Smarthome
+ ID_OUI_FROM_DATABASE=Smartlabs, Inc.
 
 OUI:000EF4*
  ID_OUI_FROM_DATABASE=Kasda Networks Inc
@@ -14010,7 +14010,7 @@ OUI:001257*
  ID_OUI_FROM_DATABASE=LeapComm Communication Technologies Inc.
 
 OUI:001258*
- ID_OUI_FROM_DATABASE=Activis Polska
+ ID_OUI_FROM_DATABASE=TechVoIP Sp z o.o.
 
 OUI:001259*
  ID_OUI_FROM_DATABASE=THERMO ELECTRON KARLSRUHE
@@ -14472,7 +14472,7 @@ OUI:0012F1*
  ID_OUI_FROM_DATABASE=IFOTEC
 
 OUI:0012F2*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:0012F3*
  ID_OUI_FROM_DATABASE=connectBlue AB
@@ -15885,7 +15885,7 @@ OUI:0014C8*
  ID_OUI_FROM_DATABASE=Contemporary Research Corp
 
 OUI:0014C9*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:0014CA*
  ID_OUI_FROM_DATABASE=Key Radio Systems Limited
@@ -19479,7 +19479,7 @@ OUI:001976*
  ID_OUI_FROM_DATABASE=Xipher Technologies, LLC
 
 OUI:001977*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
 
 OUI:001978*
  ID_OUI_FROM_DATABASE=Datum Systems, Inc.
@@ -21057,7 +21057,7 @@ OUI:001B84*
  ID_OUI_FROM_DATABASE=Scan Engineering Telecom
 
 OUI:001B85*
- ID_OUI_FROM_DATABASE=MAN Diesel SE
+ ID_OUI_FROM_DATABASE=MAN Energy Solutions
 
 OUI:001B86*
  ID_OUI_FROM_DATABASE=Bosch Access Systems GmbH
@@ -21969,7 +21969,7 @@ OUI:001BEC*
  ID_OUI_FROM_DATABASE=Netio Technologies Co., Ltd
 
 OUI:001BED*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:001BEE*
  ID_OUI_FROM_DATABASE=Nokia Danmark A/S
@@ -28335,7 +28335,7 @@ OUI:002437*
  ID_OUI_FROM_DATABASE=Motorola - BSG
 
 OUI:002438*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:002439*
  ID_OUI_FROM_DATABASE=Digital Barriers Advanced Technologies
@@ -30030,7 +30030,7 @@ OUI:002673*
  ID_OUI_FROM_DATABASE=RICOH COMPANY,LTD.
 
 OUI:002674*
- ID_OUI_FROM_DATABASE=Electronic Solutions, Inc.
+ ID_OUI_FROM_DATABASE=Hunter Douglas
 
 OUI:002675*
  ID_OUI_FROM_DATABASE=Aztech Electronics Pte Ltd
@@ -30561,7 +30561,7 @@ OUI:0027E3*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
 OUI:0027F8*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:00289F*
  ID_OUI_FROM_DATABASE=Semptian Co., Ltd.
@@ -33110,6 +33110,9 @@ OUI:005D03*
 OUI:005D73*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
+OUI:005E0C*
+ ID_OUI_FROM_DATABASE=HMD Global Oy
+
 OUI:005F86*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
@@ -33429,7 +33432,7 @@ OUI:006068*
  ID_OUI_FROM_DATABASE=Dialogic Corporation
 
 OUI:006069*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:00606A*
  ID_OUI_FROM_DATABASE=MITSUBISHI WIRELESS COMMUNICATIONS. INC.
@@ -33783,7 +33786,7 @@ OUI:0060DE*
  ID_OUI_FROM_DATABASE=Kayser-Threde GmbH
 
 OUI:0060DF*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:0060E0*
  ID_OUI_FROM_DATABASE=AXIOM TECHNOLOGY CO., LTD.
@@ -33893,6 +33896,9 @@ OUI:006440*
 OUI:0064A6*
  ID_OUI_FROM_DATABASE=Maquet CardioVascular
 
+OUI:006619*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:00664B*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -33902,6 +33908,9 @@ OUI:006762*
 OUI:0068EB*
  ID_OUI_FROM_DATABASE=HP Inc.
 
+OUI:00692D*
+ ID_OUI_FROM_DATABASE=Sunnovo International Limited
+
 OUI:0069670*
  ID_OUI_FROM_DATABASE=Annapurna labs
 
@@ -34856,6 +34865,9 @@ OUI:008764*
 OUI:008865*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:0088BA*
+ ID_OUI_FROM_DATABASE=NC&C
+
 OUI:008A96*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
@@ -35669,6 +35681,9 @@ OUI:009363*
 OUI:0094A1*
  ID_OUI_FROM_DATABASE=F5 Networks, Inc.
 
+OUI:0094EC*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:009569*
  ID_OUI_FROM_DATABASE=LSD Science and Technology Co.,Ltd.
 
@@ -35696,6 +35711,9 @@ OUI:009E1E*
 OUI:009EC8*
  ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
 
+OUI:009EEE*
+ ID_OUI_FROM_DATABASE=Positivo Tecnologia S.A.
+
 OUI:00A000*
  ID_OUI_FROM_DATABASE=CENTILLION NETWORKS, INC.
 
@@ -36533,6 +36551,9 @@ OUI:00AD24*
 OUI:00AD63*
  ID_OUI_FROM_DATABASE=Dedicated Micros Malta LTD
 
+OUI:00ADD5*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:00AECD*
  ID_OUI_FROM_DATABASE=Pensando Systems
 
@@ -36692,6 +36713,9 @@ OUI:00B771*
 OUI:00B78D*
  ID_OUI_FROM_DATABASE=Nanjing Shining Electric Automation Co., Ltd
 
+OUI:00B7A8*
+ ID_OUI_FROM_DATABASE=Heinzinger electronic GmbH
+
 OUI:00B810*
  ID_OUI_FROM_DATABASE=Yichip Microelectronics (Hangzhou) Co.,Ltd
 
@@ -38697,7 +38721,7 @@ OUI:00E051*
  ID_OUI_FROM_DATABASE=TALX CORPORATION
 
 OUI:00E052*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:00E053*
  ID_OUI_FROM_DATABASE=CELLPORT LABS, INC.
@@ -39467,6 +39491,9 @@ OUI:041EFA*
 OUI:04209A*
  ID_OUI_FROM_DATABASE=Panasonic Corporation AVC Networks Company
 
+OUI:042144*
+ ID_OUI_FROM_DATABASE=Sunitec Enterprise Co.,Ltd
+
 OUI:04214C*
  ID_OUI_FROM_DATABASE=Insight Energy Ventures LLC
 
@@ -39629,6 +39656,9 @@ OUI:045EA4*
 OUI:045FA7*
  ID_OUI_FROM_DATABASE=Shenzhen Yichen Technology Development Co.,LTD
 
+OUI:045FB9*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
 OUI:046169*
  ID_OUI_FROM_DATABASE=MEDIA GLOBAL LINKS CO., LTD.
 
@@ -39782,6 +39812,9 @@ OUI:048B42*
 OUI:048C03*
  ID_OUI_FROM_DATABASE=ThinPAD Technology (Shenzhen)CO.,LTD
 
+OUI:048C16*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:048C9A*
  ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
 
@@ -39905,6 +39938,9 @@ OUI:04BD70*
 OUI:04BD88*
  ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
 
+OUI:04BDBF*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:04BF6D*
  ID_OUI_FROM_DATABASE=Zyxel Communications Corporation
 
@@ -40175,12 +40211,18 @@ OUI:04F128*
 OUI:04F13E*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:04F169*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:04F17D*
  ID_OUI_FROM_DATABASE=Tarana Wireless
 
 OUI:04F4BC*
  ID_OUI_FROM_DATABASE=Xena Networks
 
+OUI:04F5F4*
+ ID_OUI_FROM_DATABASE=Proxim Wireless
+
 OUI:04F7E4*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -40611,7 +40653,7 @@ OUI:080087*
  ID_OUI_FROM_DATABASE=Xyplex, Inc.
 
 OUI:080088*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:080089*
  ID_OUI_FROM_DATABASE=Kinetics
@@ -40643,6 +40685,9 @@ OUI:08010F*
 OUI:08028E*
  ID_OUI_FROM_DATABASE=NETGEAR
 
+OUI:080342*
+ ID_OUI_FROM_DATABASE=Palo Alto Networks
+
 OUI:080371*
  ID_OUI_FROM_DATABASE=KRG CORPORATE
 
@@ -40919,6 +40964,9 @@ OUI:0868EA*
 OUI:086A0A*
  ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP
 
+OUI:086BD1*
+ ID_OUI_FROM_DATABASE=Shenzhen SuperElectron Technology Co.,Ltd.
+
 OUI:086BD7*
  ID_OUI_FROM_DATABASE=Silicon Laboratories
 
@@ -40997,6 +41045,9 @@ OUI:088620*
 OUI:08863B*
  ID_OUI_FROM_DATABASE=Belkin International Inc.
 
+OUI:0887C6*
+ ID_OUI_FROM_DATABASE=INGRAM MICRO SERVICES
+
 OUI:088C2C*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
@@ -41063,6 +41114,9 @@ OUI:08A8A1*
 OUI:08A95A*
  ID_OUI_FROM_DATABASE=AzureWave Technology Inc.
 
+OUI:08AA55*
+ ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
+
 OUI:08ACA5*
  ID_OUI_FROM_DATABASE=Benu Video, Inc.
 
@@ -41072,6 +41126,9 @@ OUI:08AED6*
 OUI:08AF78*
  ID_OUI_FROM_DATABASE=Totus Solutions, Inc.
 
+OUI:08B055*
+ ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP
+
 OUI:08B258*
  ID_OUI_FROM_DATABASE=Juniper Networks
 
@@ -41114,9 +41171,15 @@ OUI:08BE77*
 OUI:08BEAC*
  ID_OUI_FROM_DATABASE=Edimax Technology Co. Ltd.
 
+OUI:08BFA0*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:08C021*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
+OUI:08C0EB*
+ ID_OUI_FROM_DATABASE=Mellanox Technologies, Inc.
+
 OUI:08C5E1*
  ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND)
 
@@ -41193,7 +41256,7 @@ OUI:08EA40*
  ID_OUI_FROM_DATABASE=SHENZHEN BILIAN ELECTRONIC CO.,LTD
 
 OUI:08EA44*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
 
 OUI:08EB29*
  ID_OUI_FROM_DATABASE=Jiangsu Huitong Group Co.,Ltd.
@@ -41279,6 +41342,9 @@ OUI:08F1EA*
 OUI:08F2F4*
  ID_OUI_FROM_DATABASE=Net One Partners Co.,Ltd.
 
+OUI:08F458*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:08F4AB*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -41339,6 +41405,9 @@ OUI:0C130B*
 OUI:0C1420*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:0C14D2*
+ ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd.
+
 OUI:0C1539*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -41390,6 +41459,9 @@ OUI:0C2724*
 OUI:0C2755*
  ID_OUI_FROM_DATABASE=Valuable Techologies Limited
 
+OUI:0C29EF*
+ ID_OUI_FROM_DATABASE=Dell Inc.
+
 OUI:0C2A69*
  ID_OUI_FROM_DATABASE=electric imp, incorporated
 
@@ -41411,9 +41483,15 @@ OUI:0C2FB0*
 OUI:0C3021*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:0C35FE*
+ ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
+
 OUI:0C3747*
  ID_OUI_FROM_DATABASE=zte corporation
 
+OUI:0C3796*
+ ID_OUI_FROM_DATABASE=BIZLINK TECHNOLOGY, INC.
+
 OUI:0C37DC*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -41459,6 +41537,9 @@ OUI:0C47C9*
 OUI:0C4885*
  ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
 
+OUI:0C48C6*
+ ID_OUI_FROM_DATABASE=CELESTICA INC.
+
 OUI:0C4933*
  ID_OUI_FROM_DATABASE=Sichuan Jiuzhou Electronic Technology Co., Ltd.
 
@@ -41648,6 +41729,9 @@ OUI:0C8112*
 OUI:0C8126*
  ID_OUI_FROM_DATABASE=Juniper Networks
 
+OUI:0C817D*
+ ID_OUI_FROM_DATABASE=EEP Elektro-Elektronik Pranjic GmbH
+
 OUI:0C8230*
  ID_OUI_FROM_DATABASE=SHENZHEN MAGNUS TECHNOLOGIES CO.,LTD
 
@@ -41657,6 +41741,9 @@ OUI:0C8268*
 OUI:0C826A*
  ID_OUI_FROM_DATABASE=Wuhan Huagong Genuine Optics Technology Co., Ltd
 
+OUI:0C839A*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:0C8411*
  ID_OUI_FROM_DATABASE=A.O. Smith Water Products
 
@@ -42140,6 +42227,12 @@ OUI:1005B1*
 OUI:1005CA*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
+OUI:1005E1*
+ ID_OUI_FROM_DATABASE=Nokia
+
+OUI:100645*
+ ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+
 OUI:1007230*
  ID_OUI_FROM_DATABASE=RippleTek Tech Ltd
 
@@ -42278,6 +42371,9 @@ OUI:102831*
 OUI:102959*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:1029AB*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:102AB3*
  ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
 
@@ -42323,6 +42419,9 @@ OUI:10364A*
 OUI:103711*
  ID_OUI_FROM_DATABASE=Simlink AS
 
+OUI:103917*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:1039E9*
  ID_OUI_FROM_DATABASE=Juniper Networks
 
@@ -42392,6 +42491,9 @@ OUI:104F58*
 OUI:104FA8*
  ID_OUI_FROM_DATABASE=Sony Corporation
 
+OUI:105072*
+ ID_OUI_FROM_DATABASE=Sercomm Corporation.
+
 OUI:105172*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -42476,6 +42578,9 @@ OUI:106F3F*
 OUI:106FEF*
  ID_OUI_FROM_DATABASE=Ad-Sol Nissin Corp
 
+OUI:1070FD*
+ ID_OUI_FROM_DATABASE=Mellanox Technologies, Inc.
+
 OUI:1071F9*
  ID_OUI_FROM_DATABASE=Cloud Telecomputers, LLC
 
@@ -42686,6 +42791,9 @@ OUI:10C2BA*
 OUI:10C37B*
  ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC.
 
+OUI:10C3AB*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:10C586*
  ID_OUI_FROM_DATABASE=BIO SOUND LAB CO., LTD.
 
@@ -42731,6 +42839,9 @@ OUI:10CDAE*
 OUI:10CDB6*
  ID_OUI_FROM_DATABASE=Essential Products, Inc.
 
+OUI:10CE45*
+ ID_OUI_FROM_DATABASE=Miromico AG
+
 OUI:10CEA9*
  ID_OUI_FROM_DATABASE=Texas Instruments
 
@@ -42899,6 +43010,9 @@ OUI:1100AA*
 OUI:111111*
  ID_OUI_FROM_DATABASE=Private
 
+OUI:140152*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:1402EC*
  ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise
 
@@ -42959,6 +43073,9 @@ OUI:1414E6*
 OUI:14157C*
  ID_OUI_FROM_DATABASE=TOKYO COSMOS ELECTRIC CO.,LTD.
 
+OUI:14169D*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
 OUI:14169E*
  ID_OUI_FROM_DATABASE=Wingtech Group (HongKong)Limited
 
@@ -43142,6 +43259,9 @@ OUI:14444A*
 OUI:1446E4*
  ID_OUI_FROM_DATABASE=AVISTEL
 
+OUI:14472D*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
 OUI:144802*
  ID_OUI_FROM_DATABASE=THE YEOLRIM Co.,Ltd.
 
@@ -43254,7 +43374,7 @@ OUI:145BE1*
  ID_OUI_FROM_DATABASE=nyantec GmbH
 
 OUI:145E45*
- ID_OUI_FROM_DATABASE=Kaleao Limited
+ ID_OUI_FROM_DATABASE=Bamboo Systems Group
 
 OUI:145F94*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -43298,6 +43418,9 @@ OUI:147411*
 OUI:147590*
  ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
 
+OUI:147740*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:14780B*
  ID_OUI_FROM_DATABASE=Varex Imaging Deutschland AG
 
@@ -43439,6 +43562,51 @@ OUI:14ABF0*
 OUI:14ADCA*
  ID_OUI_FROM_DATABASE=China Mobile Iot Limited company
 
+OUI:14AE850*
+ ID_OUI_FROM_DATABASE=Kayamatics Limited
+
+OUI:14AE851*
+ ID_OUI_FROM_DATABASE=Henfred Technology Co., Ltd.
+
+OUI:14AE852*
+ ID_OUI_FROM_DATABASE=Qingdao iTechene Technologies Co., Ltd.
+
+OUI:14AE853*
+ ID_OUI_FROM_DATABASE=IFLYTEK CO.,LTD.
+
+OUI:14AE854*
+ ID_OUI_FROM_DATABASE=CENTERVUE SPA
+
+OUI:14AE855*
+ ID_OUI_FROM_DATABASE=AZ-TECHNOLOGY SDN BHD
+
+OUI:14AE856*
+ ID_OUI_FROM_DATABASE=TMG TE GmbH
+
+OUI:14AE857*
+ ID_OUI_FROM_DATABASE=SHENZHEN HONOR ELECTRONIC CO.,LTD
+
+OUI:14AE858*
+ ID_OUI_FROM_DATABASE=Trimble LEM
+
+OUI:14AE859*
+ ID_OUI_FROM_DATABASE=Veo Technologies
+
+OUI:14AE85A*
+ ID_OUI_FROM_DATABASE=MTA Systems
+
+OUI:14AE85B*
+ ID_OUI_FROM_DATABASE=NTC SOFT
+
+OUI:14AE85C*
+ ID_OUI_FROM_DATABASE=IO Industries Inc.
+
+OUI:14AE85D*
+ ID_OUI_FROM_DATABASE=iSolution Technologies Co.,Ltd.
+
+OUI:14AE85E*
+ ID_OUI_FROM_DATABASE=Sercomm Corporation.
+
 OUI:14AEDB*
  ID_OUI_FROM_DATABASE=VTech Telecommunications Ltd.
 
@@ -43550,6 +43718,9 @@ OUI:14DDA9*
 OUI:14DDE5*
  ID_OUI_FROM_DATABASE=MPMKVVCL
 
+OUI:14DE39*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:14E4EC*
  ID_OUI_FROM_DATABASE=mLogic LLC
 
@@ -43835,6 +44006,9 @@ OUI:184617*
 OUI:184644*
  ID_OUI_FROM_DATABASE=Home Control Singapore Pte Ltd
 
+OUI:1848CA*
+ ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd.
+
 OUI:1848D8*
  ID_OUI_FROM_DATABASE=Fastback Networks
 
@@ -43853,6 +44027,9 @@ OUI:184C08*
 OUI:184E94*
  ID_OUI_FROM_DATABASE=MESSOA TECHNOLOGIES INC.
 
+OUI:184ECB*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:184F32*
  ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
 
@@ -43886,6 +44063,9 @@ OUI:185644*
 OUI:185680*
  ID_OUI_FROM_DATABASE=Intel Corporate
 
+OUI:185869*
+ ID_OUI_FROM_DATABASE=Sailer Electronic Co., Ltd
+
 OUI:185933*
  ID_OUI_FROM_DATABASE=Cisco SPVTG
 
@@ -43952,12 +44132,18 @@ OUI:186882*
 OUI:1868CB*
  ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd.
 
+OUI:1869D8*
+ ID_OUI_FROM_DATABASE=HANGZHOU AIXIANGJI TECHNOLOGY CO., LTD
+
 OUI:1869DA*
  ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd.
 
 OUI:186D99*
  ID_OUI_FROM_DATABASE=Adanis Inc.
 
+OUI:186F2D*
+ ID_OUI_FROM_DATABASE=Shenzhen Sundray Technologies Company Limited
+
 OUI:18703B*
  ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
 
@@ -43988,6 +44174,9 @@ OUI:187C0B*
 OUI:187C81*
  ID_OUI_FROM_DATABASE=Valeo Vision Systems
 
+OUI:187EB9*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
 OUI:187ED5*
  ID_OUI_FROM_DATABASE=shenzhen kaism technology Co. Ltd
 
@@ -44006,6 +44195,9 @@ OUI:18810E*
 OUI:188219*
  ID_OUI_FROM_DATABASE=Alibaba Cloud Computing Ltd.
 
+OUI:18828C*
+ ID_OUI_FROM_DATABASE=Arcadyan Corporation
+
 OUI:188331*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
@@ -44036,6 +44228,9 @@ OUI:1889A0*
 OUI:1889DF*
  ID_OUI_FROM_DATABASE=CerebrEX Inc.
 
+OUI:188A6A*
+ ID_OUI_FROM_DATABASE=AVPro Global Hldgs
+
 OUI:188B15*
  ID_OUI_FROM_DATABASE=ShenZhen ZhongRuiJing Technology co.,LTD
 
@@ -44132,6 +44327,9 @@ OUI:189C27*
 OUI:189C5D*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
+OUI:189E2C*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:189EFC*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -44294,6 +44492,9 @@ OUI:18D717*
 OUI:18D949*
  ID_OUI_FROM_DATABASE=Qvis Labs, LLC
 
+OUI:18D98F*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:18D9EF*
  ID_OUI_FROM_DATABASE=Shuttle Inc.
 
@@ -44402,6 +44603,12 @@ OUI:18FF2E*
 OUI:1C0042*
  ID_OUI_FROM_DATABASE=NARI Technology Co., Ltd.
 
+OUI:1C0219*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
+OUI:1C05B7*
+ ID_OUI_FROM_DATABASE=Chongqing Trantor Technology Co., Ltd.
+
 OUI:1C0656*
  ID_OUI_FROM_DATABASE=IDY Corporation
 
@@ -44429,6 +44636,9 @@ OUI:1C129D*
 OUI:1C12B0*
  ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
 
+OUI:1C1338*
+ ID_OUI_FROM_DATABASE=Kimball Electronics Group, LLC
+
 OUI:1C1386*
  ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
 
@@ -44483,6 +44693,9 @@ OUI:1C1EE3*
 OUI:1C1FD4*
  ID_OUI_FROM_DATABASE=LifeBEAM Technologies LTD
 
+OUI:1C1FF1*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:1C20DB*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -44726,6 +44939,9 @@ OUI:1C62B8*
 OUI:1C63B7*
  ID_OUI_FROM_DATABASE=OpenProducts 237 AB
 
+OUI:1C63BF*
+ ID_OUI_FROM_DATABASE=SHENZHEN BROADTEL  TELECOM CO.,LTD
+
 OUI:1C6499*
  ID_OUI_FROM_DATABASE=Comtrend Corporation
 
@@ -45095,6 +45311,9 @@ OUI:1C965A*
 OUI:1C973D*
  ID_OUI_FROM_DATABASE=PRICOM Design
 
+OUI:1C97C5*
+ ID_OUI_FROM_DATABASE=Ynomia Pty Ltd
+
 OUI:1C98EC*
  ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise
 
@@ -45755,6 +45974,9 @@ OUI:204E71*
 OUI:204E7F*
  ID_OUI_FROM_DATABASE=NETGEAR
 
+OUI:2050E7*
+ ID_OUI_FROM_DATABASE=AMPAK Technology,Inc.
+
 OUI:2053CA*
  ID_OUI_FROM_DATABASE=Risk Technology Ltd
 
@@ -45834,7 +46056,10 @@ OUI:206BE7*
  ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
 
 OUI:206C8A*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
+
+OUI:206D31*
+ ID_OUI_FROM_DATABASE=FIREWALLA INC
 
 OUI:206E9C*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -45887,6 +46112,9 @@ OUI:207D74*
 OUI:208058*
  ID_OUI_FROM_DATABASE=Ciena Corporation
 
+OUI:20826A*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
 OUI:2082C0*
  ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
 
@@ -45986,6 +46214,9 @@ OUI:209BA5*
 OUI:209BCD*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:209EF7*
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
+
 OUI:20A2E4*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -46160,6 +46391,9 @@ OUI:20E564*
 OUI:20E791*
  ID_OUI_FROM_DATABASE=Siemens Healthcare Diagnostics, Inc
 
+OUI:20E874*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
 OUI:20E882*
  ID_OUI_FROM_DATABASE=zte corporation
 
@@ -46334,6 +46568,9 @@ OUI:241510E*
 OUI:24166D*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
+OUI:24169D*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
 OUI:24181D*
  ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND)
 
@@ -46424,6 +46661,9 @@ OUI:24418C*
 OUI:2442BC*
  ID_OUI_FROM_DATABASE=Alinco,incorporated
 
+OUI:2443E2*
+ ID_OUI_FROM_DATABASE=DASAN Network Solutions
+
 OUI:244427*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -46547,6 +46787,9 @@ OUI:246278*
 OUI:2462AB*
  ID_OUI_FROM_DATABASE=Espressif Inc.
 
+OUI:2462CE*
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
+
 OUI:2464EF*
  ID_OUI_FROM_DATABASE=CYG SUNRI CO.,LTD.
 
@@ -46556,6 +46799,9 @@ OUI:246511*
 OUI:246880*
  ID_OUI_FROM_DATABASE=Braveridge.co.,ltd.
 
+OUI:2468B0*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:24693E*
  ID_OUI_FROM_DATABASE=innodisk Corporation
 
@@ -46787,6 +47033,9 @@ OUI:24C848*
 OUI:24C86E*
  ID_OUI_FROM_DATABASE=Chaney Instrument Co.
 
+OUI:24C8D3*
+ ID_OUI_FROM_DATABASE=McWane India Pvt Ltd
+
 OUI:24C9A1*
  ID_OUI_FROM_DATABASE=Ruckus Wireless
 
@@ -46802,6 +47051,9 @@ OUI:24CBE7*
 OUI:24CF21*
  ID_OUI_FROM_DATABASE=Shenzhen State Micro Technology Co., Ltd
 
+OUI:24D0DF*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
 OUI:24D13F*
  ID_OUI_FROM_DATABASE=MEXUS CO.,LTD
 
@@ -46851,7 +47103,7 @@ OUI:24DFA7*
  ID_OUI_FROM_DATABASE=Hangzhou BroadLink Technology Co.,Ltd
 
 OUI:24E124*
- ID_OUI_FROM_DATABASE=Xiamen Ursaconn Technology Co. , Ltd.
+ ID_OUI_FROM_DATABASE=Xiamen Ursalink Technology Co., Ltd.
 
 OUI:24E271*
  ID_OUI_FROM_DATABASE=Qingdao Hisense Communications Co.,Ltd.
@@ -46979,6 +47231,9 @@ OUI:28107B*
 OUI:2811A5*
  ID_OUI_FROM_DATABASE=Bose Corporation
 
+OUI:2811EC*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:281471*
  ID_OUI_FROM_DATABASE=Lantis co., LTD.
 
@@ -47096,6 +47351,9 @@ OUI:283152*
 OUI:283166*
  ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
 
+OUI:28317E*
+ ID_OUI_FROM_DATABASE=Hongkong Nano IC Technologies Co., Ltd
+
 OUI:2832C5*
  ID_OUI_FROM_DATABASE=HUMAX Co., Ltd.
 
@@ -47237,11 +47495,14 @@ OUI:2852E0*
 OUI:2852F9*
  ID_OUI_FROM_DATABASE=Zhongxin Intelligent Times (Shenzhen) Co., Ltd.
 
+OUI:285471*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:28565A*
  ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
 
 OUI:2856C1*
- ID_OUI_FROM_DATABASE=Harman International
+ ID_OUI_FROM_DATABASE=Harman/Becker Automotive Systems GmbH
 
 OUI:285767*
  ID_OUI_FROM_DATABASE=Dish Technologies Corp
@@ -47270,6 +47531,9 @@ OUI:286336*
 OUI:2863BD*
  ID_OUI_FROM_DATABASE=APTIV SERVICES US, LLC
 
+OUI:2864B0*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:28656B*
  ID_OUI_FROM_DATABASE=Keystone Microtech Corporation
 
@@ -47612,6 +47876,9 @@ OUI:28E31F*
 OUI:28E347*
  ID_OUI_FROM_DATABASE=Liteon Technology Corporation
 
+OUI:28E34E*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:28E476*
  ID_OUI_FROM_DATABASE=Pi-Coral
 
@@ -47654,6 +47921,9 @@ OUI:28EED3*
 OUI:28EF01*
  ID_OUI_FROM_DATABASE=Private
 
+OUI:28F033*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
 OUI:28F076*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -47717,6 +47987,9 @@ OUI:28F537E*
 OUI:28F606*
  ID_OUI_FROM_DATABASE=Syes srl
 
+OUI:28FA7A*
+ ID_OUI_FROM_DATABASE=Zhejiang Tmall Technology Co., Ltd.
+
 OUI:28FAA0*
  ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
 
@@ -47897,6 +48170,9 @@ OUI:2C18AE*
 OUI:2C1984*
  ID_OUI_FROM_DATABASE=IDN Telecom, Inc.
 
+OUI:2C1A01*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:2C1A31*
  ID_OUI_FROM_DATABASE=Electronics Company Limited
 
@@ -48458,6 +48734,9 @@ OUI:2C9717*
 OUI:2C97B1*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
+OUI:2C97ED*
+ ID_OUI_FROM_DATABASE=Sony Imaging Products & Solutions Inc.
+
 OUI:2C9924*
  ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
 
@@ -48476,6 +48755,9 @@ OUI:2C9EEC*
 OUI:2C9EFC*
  ID_OUI_FROM_DATABASE=CANON INC.
 
+OUI:2C9FFB*
+ ID_OUI_FROM_DATABASE=Wistron Neweb Corporation
+
 OUI:2CA02F*
  ID_OUI_FROM_DATABASE=Veroguard Systems Pty Ltd
 
@@ -48572,6 +48854,9 @@ OUI:2CC260*
 OUI:2CC407*
  ID_OUI_FROM_DATABASE=machineQ
 
+OUI:2CC546*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:2CC548*
  ID_OUI_FROM_DATABASE=IAdea Corporation
 
@@ -48701,6 +48986,9 @@ OUI:2CE6CC*
 OUI:2CE871*
  ID_OUI_FROM_DATABASE=Alert Metalguard ApS
 
+OUI:2CEA7F*
+ ID_OUI_FROM_DATABASE=Dell Inc.
+
 OUI:2CEDEB*
  ID_OUI_FROM_DATABASE=Alpheus Digital Company Limited
 
@@ -49070,6 +49358,9 @@ OUI:304EC3*
 OUI:304F75*
  ID_OUI_FROM_DATABASE=DASAN Network Solutions
 
+OUI:305075*
+ ID_OUI_FROM_DATABASE=GN Audio A/S
+
 OUI:3050FD*
  ID_OUI_FROM_DATABASE=Skyworth Digital Technology(Shenzhen) Co.,Ltd
 
@@ -49184,6 +49475,9 @@ OUI:307CB2*
 OUI:307ECB*
  ID_OUI_FROM_DATABASE=SFR
 
+OUI:30809B*
+ ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd
+
 OUI:308454*
  ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
 
@@ -49305,7 +49599,10 @@ OUI:30B164*
  ID_OUI_FROM_DATABASE=Power Electronics International Inc.
 
 OUI:30B216*
- ID_OUI_FROM_DATABASE=ABB AG - Power Grids - Grid Automation
+ ID_OUI_FROM_DATABASE=ABB Power Grids Germany AG – Grid Automation
+
+OUI:30B237*
+ ID_OUI_FROM_DATABASE=GD Midea Air-Conditioning Equipment Co.,Ltd.
 
 OUI:30B3A2*
  ID_OUI_FROM_DATABASE=Shenzhen Heguang Measurement & Control Technology Co.,Ltd
@@ -49331,6 +49628,9 @@ OUI:30B64F*
 OUI:30B7D4*
  ID_OUI_FROM_DATABASE=Hitron Technologies. Inc
 
+OUI:30B9B0*
+ ID_OUI_FROM_DATABASE=Intracom Asia Co., Ltd
+
 OUI:30C01B*
  ID_OUI_FROM_DATABASE=Shenzhen Jingxun Software Telecommunication Technology Co.,Ltd
 
@@ -49472,6 +49772,9 @@ OUI:30FBB8*
 OUI:30FC68*
  ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
 
+OUI:30FCEB*
+ ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
+
 OUI:30FD11*
  ID_OUI_FROM_DATABASE=MACROTECH (USA) INC.
 
@@ -49637,6 +49940,9 @@ OUI:3413E8*
 OUI:34145F*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:3414B5*
+ ID_OUI_FROM_DATABASE=Texas Instruments
+
 OUI:341513*
  ID_OUI_FROM_DATABASE=Texas Instruments
 
@@ -49769,6 +50075,9 @@ OUI:34363B*
 OUI:343759*
  ID_OUI_FROM_DATABASE=zte corporation
 
+OUI:343794*
+ ID_OUI_FROM_DATABASE=Hamee Corp.
+
 OUI:3438AF*
  ID_OUI_FROM_DATABASE=Inlab Software GmbH
 
@@ -49832,6 +50141,9 @@ OUI:344F5C*
 OUI:344F69*
  ID_OUI_FROM_DATABASE=EKINOPS SAS
 
+OUI:345180*
+ ID_OUI_FROM_DATABASE=TCL King Electrical Appliances (Huizhou) Co., Ltd
+
 OUI:3451AA*
  ID_OUI_FROM_DATABASE=JID GLOBAL
 
@@ -49919,6 +50231,9 @@ OUI:346F92*
 OUI:346FED*
  ID_OUI_FROM_DATABASE=Enovation Controls
 
+OUI:347146*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:347563*
  ID_OUI_FROM_DATABASE=SHENZHEN RF-LINK TECHNOLOGY CO.,LTD.
 
@@ -49928,6 +50243,9 @@ OUI:3475C7*
 OUI:3476C5*
  ID_OUI_FROM_DATABASE=I-O DATA DEVICE,INC.
 
+OUI:347839*
+ ID_OUI_FROM_DATABASE=zte corporation
+
 OUI:347877*
  ID_OUI_FROM_DATABASE=O-Net Communications (Shenzhen) Limited
 
@@ -49980,7 +50298,7 @@ OUI:348446*
  ID_OUI_FROM_DATABASE=Ericsson AB
 
 OUI:348584*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
 
 OUI:34862A*
  ID_OUI_FROM_DATABASE=Heinz Lackmann GmbH & Co KG
@@ -50198,6 +50516,9 @@ OUI:34CE00*
 OUI:34CE94*
  ID_OUI_FROM_DATABASE=Parsec (Pty) Ltd
 
+OUI:34CFF6*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
 OUI:34D09B*
  ID_OUI_FROM_DATABASE=MobilMAX Technology Inc.
 
@@ -50441,6 +50762,9 @@ OUI:380025*
 OUI:380118*
  ID_OUI_FROM_DATABASE=ULVAC,Inc.
 
+OUI:380146*
+ ID_OUI_FROM_DATABASE=SHENZHEN BILIAN ELECTRONIC CO.,LTD
+
 OUI:380195*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
@@ -50507,6 +50831,9 @@ OUI:38144E*
 OUI:3816D1*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:381730*
+ ID_OUI_FROM_DATABASE=Ulrich Lippert GmbH & Co KG
+
 OUI:381766*
  ID_OUI_FROM_DATABASE=PROMZAKAZ LTD.
 
@@ -50558,6 +50885,9 @@ OUI:38229D*
 OUI:3822D6*
  ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited
 
+OUI:3822E2*
+ ID_OUI_FROM_DATABASE=HP Inc.
+
 OUI:38256B*
  ID_OUI_FROM_DATABASE=Microsoft Mobile Oy
 
@@ -50864,6 +51194,9 @@ OUI:388345*
 OUI:38839A*
  ID_OUI_FROM_DATABASE=SHENZHEN RF-LINK TECHNOLOGY CO.,LTD.
 
+OUI:388479*
+ ID_OUI_FROM_DATABASE=Cisco Meraki
+
 OUI:388602*
  ID_OUI_FROM_DATABASE=Flexoptix GmbH
 
@@ -51212,6 +51545,9 @@ OUI:38E98C*
 OUI:38EAA7*
  ID_OUI_FROM_DATABASE=Hewlett Packard
 
+OUI:38EB47*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:38EC0D*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -51257,6 +51593,9 @@ OUI:38F557*
 OUI:38F597*
  ID_OUI_FROM_DATABASE=home2net GmbH
 
+OUI:38F601*
+ ID_OUI_FROM_DATABASE=Solid State Storage Technology Corporation
+
 OUI:38F708*
  ID_OUI_FROM_DATABASE=National Resource Management, Inc.
 
@@ -51707,6 +52046,9 @@ OUI:3C57BD*
 OUI:3C57D5*
  ID_OUI_FROM_DATABASE=FiveCo
 
+OUI:3C58C2*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
 OUI:3C591E*
  ID_OUI_FROM_DATABASE=TCL King Electrical Appliances (Huizhou) Co., Ltd
 
@@ -51722,6 +52064,9 @@ OUI:3C5CC3*
 OUI:3C5CC4*
  ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
 
+OUI:3C5CF1*
+ ID_OUI_FROM_DATABASE=eero inc.
+
 OUI:3C5EC3*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
@@ -51839,12 +52184,18 @@ OUI:3C7873*
 OUI:3C7A8A*
  ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
 
+OUI:3C7D0A*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
 OUI:3C7DB1*
  ID_OUI_FROM_DATABASE=Texas Instruments
 
 OUI:3C7F6F*
  ID_OUI_FROM_DATABASE=Telechips, Inc.
 
+OUI:3C806B*
+ ID_OUI_FROM_DATABASE=Hunan Voc Acoustics Technology Co., Ltd.
+
 OUI:3C80AA*
  ID_OUI_FROM_DATABASE=Ransnet Singapore Pte Ltd
 
@@ -52004,6 +52355,9 @@ OUI:3CB15B*
 OUI:3CB17F*
  ID_OUI_FROM_DATABASE=Wattwatchers Pty Ld
 
+OUI:3CB53D*
+ ID_OUI_FROM_DATABASE=HUNAN GOKE MICROELECTRONICS CO.,LTD
+
 OUI:3CB6B7*
  ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
 
@@ -52178,6 +52532,9 @@ OUI:3CF591*
 OUI:3CF5CC*
  ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd
 
+OUI:3CF652*
+ ID_OUI_FROM_DATABASE=zte corporation
+
 OUI:3CF72A*
  ID_OUI_FROM_DATABASE=Nokia Corporation
 
@@ -52265,6 +52622,9 @@ OUI:4001C6*
 OUI:40040C*
  ID_OUI_FROM_DATABASE=A&T
 
+OUI:400589*
+ ID_OUI_FROM_DATABASE=T-Mobile, USA
+
 OUI:4006A0*
  ID_OUI_FROM_DATABASE=Texas Instruments
 
@@ -52353,7 +52713,7 @@ OUI:4017E2*
  ID_OUI_FROM_DATABASE=INTAI TECHNOLOGY CORP.
 
 OUI:4018B1*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
 
 OUI:4018D7*
  ID_OUI_FROM_DATABASE=Smartronix, Inc.
@@ -52388,6 +52748,9 @@ OUI:402814*
 OUI:402B50*
  ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
 
+OUI:402B69*
+ ID_OUI_FROM_DATABASE=Kumho Electric Inc.
+
 OUI:402BA1*
  ID_OUI_FROM_DATABASE=Sony Mobile Communications Inc
 
@@ -52439,6 +52802,12 @@ OUI:402CF4*
 OUI:402E28*
  ID_OUI_FROM_DATABASE=MiXTelematics
 
+OUI:402E71*
+ ID_OUI_FROM_DATABASE=Texas Instruments
+
+OUI:402F86*
+ ID_OUI_FROM_DATABASE=LG Innotek
+
 OUI:403004*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -52469,6 +52838,9 @@ OUI:403F8C*
 OUI:404022*
  ID_OUI_FROM_DATABASE=ZIV
 
+OUI:404028*
+ ID_OUI_FROM_DATABASE=ZIV
+
 OUI:40406B*
  ID_OUI_FROM_DATABASE=Icomera
 
@@ -52482,7 +52854,7 @@ OUI:4045DA*
  ID_OUI_FROM_DATABASE=Spreadtrum Communications (Shanghai) Co., Ltd.
 
 OUI:40476A*
- ID_OUI_FROM_DATABASE=AG Acquisition Corp. d.b.a. ASTRO Gaming
+ ID_OUI_FROM_DATABASE=Astro Gaming
 
 OUI:4048FD0*
  ID_OUI_FROM_DATABASE=BEIJING C&W ELECTRONICS(GROUP)CO.,LTD
@@ -52622,6 +52994,9 @@ OUI:40618E*
 OUI:406231*
  ID_OUI_FROM_DATABASE=GIFA
 
+OUI:406234*
+ ID_OUI_FROM_DATABASE=Telink Semiconductor (Shanghai) Co., Ltd.
+
 OUI:4062B6*
  ID_OUI_FROM_DATABASE=Tele system communication
 
@@ -52865,6 +53240,9 @@ OUI:40B2C8*
 OUI:40B30E*
  ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd.
 
+OUI:40B31E*
+ ID_OUI_FROM_DATABASE=Universal Electronics, Inc.
+
 OUI:40B395*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -52886,6 +53264,9 @@ OUI:40B688*
 OUI:40B6B1*
  ID_OUI_FROM_DATABASE=SUNGSAM CO,.Ltd
 
+OUI:40B6E7*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:40B7F3*
  ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
 
@@ -52904,6 +53285,9 @@ OUI:40BA61*
 OUI:40BC60*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:40BC68*
+ ID_OUI_FROM_DATABASE=Wuhan Funshion Online Technologies Co.,Ltd
+
 OUI:40BC73*
  ID_OUI_FROM_DATABASE=Cronoplast  S.L.
 
@@ -52985,6 +53369,9 @@ OUI:40D855*
 OUI:40DC9D*
  ID_OUI_FROM_DATABASE=HAJEN
 
+OUI:40DEAD*
+ ID_OUI_FROM_DATABASE=Juniper Networks
+
 OUI:40DF02*
  ID_OUI_FROM_DATABASE=LINE BIZ Plus
 
@@ -53003,6 +53390,9 @@ OUI:40E793*
 OUI:40EACE*
  ID_OUI_FROM_DATABASE=FOUNDER BROADBAND NETWORK SERVICE CO.,LTD
 
+OUI:40EC99*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
 OUI:40ECF8*
  ID_OUI_FROM_DATABASE=Siemens AG
 
@@ -53135,6 +53525,9 @@ OUI:40F420*
 OUI:40F4EC*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
+OUI:40F520*
+ ID_OUI_FROM_DATABASE=Espressif Inc.
+
 OUI:40F52E*
  ID_OUI_FROM_DATABASE=Leica Microsystems (Schweiz) AG
 
@@ -53222,6 +53615,9 @@ OUI:4409B8*
 OUI:440CFD*
  ID_OUI_FROM_DATABASE=NetMan Co., Ltd.
 
+OUI:4410FE*
+ ID_OUI_FROM_DATABASE=Huizhou Foryou General Electronics Co., Ltd.
+
 OUI:441102*
  ID_OUI_FROM_DATABASE=EDMI  Europe Ltd
 
@@ -53429,6 +53825,9 @@ OUI:44599F*
 OUI:4459E3*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
+OUI:445CE9*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:445D5E*
  ID_OUI_FROM_DATABASE=SHENZHEN Coolkit Technology CO.,LTD
 
@@ -53507,6 +53906,9 @@ OUI:4473D6*
 OUI:44746C*
  ID_OUI_FROM_DATABASE=Sony Mobile Communications Inc
 
+OUI:447654*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:44783E*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
@@ -53561,6 +53963,9 @@ OUI:448A5B*
 OUI:448C52*
  ID_OUI_FROM_DATABASE=KTIS CO., Ltd
 
+OUI:448DBF*
+ ID_OUI_FROM_DATABASE=Rhino Mobility LLC
+
 OUI:448E12*
  ID_OUI_FROM_DATABASE=DT Research, Inc.
 
@@ -53699,6 +54104,9 @@ OUI:44C4A9*
 OUI:44C56F*
  ID_OUI_FROM_DATABASE=NGN Easy Satfinder (Tianjin) Electronic Co., Ltd
 
+OUI:44C65D*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
 OUI:44C69B*
  ID_OUI_FROM_DATABASE=Wuhan Feng Tian Information Network CO.,LTD
 
@@ -54233,6 +54641,9 @@ OUI:486B91*
 OUI:486DBB*
  ID_OUI_FROM_DATABASE=Vestel Elektronik San ve Tic. A.Ş.
 
+OUI:486E70*
+ ID_OUI_FROM_DATABASE=Zhejiang Tmall Technology Co., Ltd.
+
 OUI:486E73*
  ID_OUI_FROM_DATABASE=Pica8, Inc.
 
@@ -54266,8 +54677,14 @@ OUI:487A55*
 OUI:487ADA*
  ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited
 
+OUI:487AF6*
+ ID_OUI_FROM_DATABASE=NCS ELECTRICAL SDN BHD
+
+OUI:487AFF*
+ ID_OUI_FROM_DATABASE=ESSYS
+
 OUI:487B5E*
- ID_OUI_FROM_DATABASE=Social Mobile
+ ID_OUI_FROM_DATABASE=SMT TELECOMM HK
 
 OUI:487B6B*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
@@ -54320,6 +54737,9 @@ OUI:488E42*
 OUI:488EEF*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
+OUI:488F5A*
+ ID_OUI_FROM_DATABASE=Routerboard.com
+
 OUI:489153*
  ID_OUI_FROM_DATABASE=Weinmann Geräte für Medizin GmbH + Co. KG
 
@@ -54362,6 +54782,9 @@ OUI:48A22D*
 OUI:48A2B7*
  ID_OUI_FROM_DATABASE=Kodofon JSC
 
+OUI:48A2E6*
+ ID_OUI_FROM_DATABASE=Resideo
+
 OUI:48A380*
  ID_OUI_FROM_DATABASE=Gionee Communication Equipment Co.,Ltd.
 
@@ -54410,6 +54833,9 @@ OUI:48B5A7*
 OUI:48B620*
  ID_OUI_FROM_DATABASE=ROLI Ltd.
 
+OUI:48B8A3*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
 OUI:48B8DE*
  ID_OUI_FROM_DATABASE=HOMEWINS TECHNOLOGY CO.,LTD.
 
@@ -54662,6 +55088,9 @@ OUI:4C09B4*
 OUI:4C09D4*
  ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation
 
+OUI:4C0A3D*
+ ID_OUI_FROM_DATABASE=ADNACOM INC.
+
 OUI:4C0B3A*
  ID_OUI_FROM_DATABASE=TCT mobile ltd
 
@@ -54773,6 +55202,9 @@ OUI:4C3275*
 OUI:4C32D9*
  ID_OUI_FROM_DATABASE=M Rutty Holdings Pty. Ltd.
 
+OUI:4C3329*
+ ID_OUI_FROM_DATABASE=Sweroam
+
 OUI:4C334E*
  ID_OUI_FROM_DATABASE=HIGHTECH
 
@@ -54803,6 +55235,12 @@ OUI:4C3C16*
 OUI:4C3FD3*
  ID_OUI_FROM_DATABASE=Texas Instruments
 
+OUI:4C4088*
+ ID_OUI_FROM_DATABASE=SANSHIN ELECTRONICS CO.,LTD.
+
+OUI:4C4576*
+ ID_OUI_FROM_DATABASE=China Mobile(Hangzhou) Information Technology Co.,Ltd.
+
 OUI:4C48DA*
  ID_OUI_FROM_DATABASE=Beijing Autelan Technology Co.,Ltd
 
@@ -55155,7 +55593,7 @@ OUI:4C9EFF*
  ID_OUI_FROM_DATABASE=Zyxel Communications Corporation
 
 OUI:4CA003*
- ID_OUI_FROM_DATABASE=T-21 Technologies LLC
+ ID_OUI_FROM_DATABASE=VITEC
 
 OUI:4CA161*
  ID_OUI_FROM_DATABASE=Rain Bird Corporation
@@ -55166,6 +55604,9 @@ OUI:4CA515*
 OUI:4CA56D*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:4CA64D*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
 OUI:4CA74B*
  ID_OUI_FROM_DATABASE=Alcatel Lucent
 
@@ -55232,6 +55673,9 @@ OUI:4CB82C*
 OUI:4CB8B5*
  ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd
 
+OUI:4CB911*
+ ID_OUI_FROM_DATABASE=Raisecom Technology CO.,LTD
+
 OUI:4CB9C8*
  ID_OUI_FROM_DATABASE=CONET CO., LTD.
 
@@ -55313,6 +55757,9 @@ OUI:4CC206*
 OUI:4CC452*
  ID_OUI_FROM_DATABASE=Shang Hai Tyd. Electon Technology Ltd.
 
+OUI:4CC53E*
+ ID_OUI_FROM_DATABASE=Zyxel Communications Corporation
+
 OUI:4CC602*
  ID_OUI_FROM_DATABASE=Radios, Inc.
 
@@ -55340,6 +55787,9 @@ OUI:4CCC34*
 OUI:4CCC6A*
  ID_OUI_FROM_DATABASE=Micro-Star INTL CO., LTD.
 
+OUI:4CCE2D*
+ ID_OUI_FROM_DATABASE=Danlaw Inc
+
 OUI:4CD08A*
  ID_OUI_FROM_DATABASE=HUMAX Co., Ltd.
 
@@ -55499,6 +55949,9 @@ OUI:4CFB45*
 OUI:4CFBFE*
  ID_OUI_FROM_DATABASE=Sercomm Japan Corporation
 
+OUI:4CFCAA*
+ ID_OUI_FROM_DATABASE=Tesla,Inc.
+
 OUI:4CFF12*
  ID_OUI_FROM_DATABASE=Fuze Entertainment Co., ltd
 
@@ -55637,6 +56090,9 @@ OUI:501E2D*
 OUI:50206B*
  ID_OUI_FROM_DATABASE=Emerson Climate Technologies Transportation Solutions
 
+OUI:5021EC*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:502267*
  ID_OUI_FROM_DATABASE=PixeLINK
 
@@ -55706,6 +56162,9 @@ OUI:50338B*
 OUI:5033F0*
  ID_OUI_FROM_DATABASE=YICHEN (SHENZHEN) TECHNOLOGY CO.LTD
 
+OUI:50382F*
+ ID_OUI_FROM_DATABASE=ASE Group Chung-Li
+
 OUI:503955*
  ID_OUI_FROM_DATABASE=Cisco SPVTG
 
@@ -55751,6 +56210,9 @@ OUI:5043B9*
 OUI:5045F7*
  ID_OUI_FROM_DATABASE=Liuhe Intelligence Technology Ltd.
 
+OUI:50464A*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:50465D*
  ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC.
 
@@ -55844,6 +56306,9 @@ OUI:5061BF*
 OUI:5061D6*
  ID_OUI_FROM_DATABASE=Indu-Sol GmbH
 
+OUI:5061F6*
+ ID_OUI_FROM_DATABASE=Universal Electronics, Inc.
+
 OUI:5062550*
  ID_OUI_FROM_DATABASE=Ufanet SC
 
@@ -56346,7 +56811,7 @@ OUI:50EAD6*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
 OUI:50EB1A*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:50EB71*
  ID_OUI_FROM_DATABASE=Intel Corporate
@@ -56454,7 +56919,7 @@ OUI:540237*
  ID_OUI_FROM_DATABASE=Teltronic AG
 
 OUI:540384*
- ID_OUI_FROM_DATABASE=Hangkong Nano IC Technologies Co., Ltd
+ ID_OUI_FROM_DATABASE=Hongkong Nano IC Technologies Co., Ltd
 
 OUI:5403F5*
  ID_OUI_FROM_DATABASE=EBN Technology Corp.
@@ -56483,6 +56948,12 @@ OUI:540955*
 OUI:54098D*
  ID_OUI_FROM_DATABASE=deister electronic GmbH
 
+OUI:540DF9*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
+OUI:540E2D*
+ ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
+
 OUI:541031*
  ID_OUI_FROM_DATABASE=SMARTO
 
@@ -56531,6 +57002,9 @@ OUI:542018*
 OUI:542160*
  ID_OUI_FROM_DATABASE=Alula
 
+OUI:54219D*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:5422F8*
  ID_OUI_FROM_DATABASE=zte corporation
 
@@ -56636,6 +57110,9 @@ OUI:544810*
 OUI:54489C*
  ID_OUI_FROM_DATABASE=CDOUBLES ELECTRONICS CO. LTD.
 
+OUI:5448E6*
+ ID_OUI_FROM_DATABASE=Beijing Xiaomi Mobile Software Co.,Ltd
+
 OUI:544A00*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
@@ -56714,6 +57191,9 @@ OUI:546C0E*
 OUI:546D52*
  ID_OUI_FROM_DATABASE=TOPVIEW OPTRONICS CORP.
 
+OUI:5471DD*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:54724F*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -56750,6 +57230,9 @@ OUI:547F54*
 OUI:547FA8*
  ID_OUI_FROM_DATABASE=TELCO systems, s.r.o.
 
+OUI:547FBC*
+ ID_OUI_FROM_DATABASE=iodyne
+
 OUI:547FEE*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
@@ -56780,6 +57263,9 @@ OUI:548922*
 OUI:548998*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
+OUI:548ABA*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
 OUI:548CA0*
  ID_OUI_FROM_DATABASE=Liteon Technology Corporation
 
@@ -56960,6 +57446,9 @@ OUI:54AB3A*
 OUI:54AE27*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:54AED0*
+ ID_OUI_FROM_DATABASE=DASAN Networks, Inc.
+
 OUI:54B121*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -57353,6 +57842,9 @@ OUI:5850AB*
 OUI:5850E6*
  ID_OUI_FROM_DATABASE=Best Buy Corporation
 
+OUI:5850ED*
+ ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd.
+
 OUI:58528A*
  ID_OUI_FROM_DATABASE=Mitsubishi Electric Corporation
 
@@ -57369,7 +57861,7 @@ OUI:58570D*
  ID_OUI_FROM_DATABASE=Danfoss Solar Inverters
 
 OUI:5859C2*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
 
 OUI:585FF6*
  ID_OUI_FROM_DATABASE=zte corporation
@@ -57515,6 +58007,9 @@ OUI:5893D8*
 OUI:58946B*
  ID_OUI_FROM_DATABASE=Intel Corporate
 
+OUI:5894B2*
+ ID_OUI_FROM_DATABASE=BrainCo
+
 OUI:5894CF*
  ID_OUI_FROM_DATABASE=Vertex Standard LMR, Inc.
 
@@ -57563,6 +58058,9 @@ OUI:58A76F*
 OUI:58A839*
  ID_OUI_FROM_DATABASE=Intel Corporate
 
+OUI:58A87B*
+ ID_OUI_FROM_DATABASE=Fitbit, Inc.
+
 OUI:58AC78*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
@@ -57884,6 +58382,9 @@ OUI:5C0CBB*
 OUI:5C0E8B*
  ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
 
+OUI:5C0FFB*
+ ID_OUI_FROM_DATABASE=Amino Communications Ltd
+
 OUI:5C1193*
  ID_OUI_FROM_DATABASE=Seal One AG
 
@@ -57926,6 +58427,9 @@ OUI:5C20D0*
 OUI:5C22C4*
  ID_OUI_FROM_DATABASE=DAE EUN ELETRONICS CO., LTD
 
+OUI:5C2316*
+ ID_OUI_FROM_DATABASE=Squirrels Research Labs LLC
+
 OUI:5C2443*
  ID_OUI_FROM_DATABASE=O-Sung Telecom Co., Ltd.
 
@@ -58043,6 +58547,9 @@ OUI:5C521E*
 OUI:5C546D*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
+OUI:5C5578*
+ ID_OUI_FROM_DATABASE=iryx corp
+
 OUI:5C56ED*
  ID_OUI_FROM_DATABASE=3pleplay Electronics Private Limited
 
@@ -58106,6 +58613,9 @@ OUI:5C6B32*
 OUI:5C6B4F*
  ID_OUI_FROM_DATABASE=Hello Inc.
 
+OUI:5C6BD7*
+ ID_OUI_FROM_DATABASE=Foshan VIOMI Electric Appliance Technology Co. Ltd.
+
 OUI:5C6D20*
  ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
 
@@ -58232,6 +58742,9 @@ OUI:5CA3EB*
 OUI:5CA48A*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
+OUI:5CA62D*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
 OUI:5CA86A*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -58262,6 +58775,9 @@ OUI:5CB13E*
 OUI:5CB15F*
  ID_OUI_FROM_DATABASE=Oceanblue Cloud Technology Limited
 
+OUI:5CB29E*
+ ID_OUI_FROM_DATABASE=ASCO Power Technologies
+
 OUI:5CB395*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -58289,6 +58805,9 @@ OUI:5CB8CB*
 OUI:5CB901*
  ID_OUI_FROM_DATABASE=Hewlett Packard
 
+OUI:5CBA2C*
+ ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise
+
 OUI:5CBA37*
  ID_OUI_FROM_DATABASE=Microsoft Corporation
 
@@ -58298,6 +58817,9 @@ OUI:5CBAEF*
 OUI:5CBD9E*
  ID_OUI_FROM_DATABASE=HONGKONG MIRACLE EAGLE TECHNOLOGY(GROUP) LIMITED
 
+OUI:5CC1D7*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:5CC213*
  ID_OUI_FROM_DATABASE=Fr. Sauter AG
 
@@ -58640,6 +59162,9 @@ OUI:601D0F*
 OUI:601D91*
  ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
 
+OUI:601D9D*
+ ID_OUI_FROM_DATABASE=Sichuan AI-Link Technology Co., Ltd.
+
 OUI:601E02*
  ID_OUI_FROM_DATABASE=EltexAlatau
 
@@ -58814,6 +59339,9 @@ OUI:6064A1*
 OUI:606720*
  ID_OUI_FROM_DATABASE=Intel Corporate
 
+OUI:60684E*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:606944*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -58862,6 +59390,9 @@ OUI:607771*
 OUI:6077E2*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:607ECD*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:607EDD*
  ID_OUI_FROM_DATABASE=Microsoft Mobile Oy
 
@@ -59004,7 +59535,7 @@ OUI:609BC8*
  ID_OUI_FROM_DATABASE=Hipad Intelligent Technology Co., Ltd.
 
 OUI:609C9F*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:609E64*
  ID_OUI_FROM_DATABASE=Vivonic GmbH
@@ -59210,6 +59741,9 @@ OUI:60D7E3E*
 OUI:60D819*
  ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
 
+OUI:60D89C*
+ ID_OUI_FROM_DATABASE=HMD Global Oy
+
 OUI:60D9A0*
  ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd.
 
@@ -59225,6 +59759,9 @@ OUI:60DA83*
 OUI:60DB2A*
  ID_OUI_FROM_DATABASE=HNS
 
+OUI:60DE35*
+ ID_OUI_FROM_DATABASE=GITSN, Inc.
+
 OUI:60DE44*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -59282,6 +59819,9 @@ OUI:60F2EF*
 OUI:60F3DA*
  ID_OUI_FROM_DATABASE=Logic Way GmbH
 
+OUI:60F43A*
+ ID_OUI_FROM_DATABASE=Edifier International
+
 OUI:60F445*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -59351,6 +59891,9 @@ OUI:64094C*
 OUI:640980*
  ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
 
+OUI:6409AC*
+ ID_OUI_FROM_DATABASE=TCT mobile ltd
+
 OUI:640B4A*
  ID_OUI_FROM_DATABASE=Digital Telecom Technology Limited
 
@@ -59579,6 +60122,9 @@ OUI:645A04*
 OUI:645AED*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:645CF3*
+ ID_OUI_FROM_DATABASE=ParanTek Inc.
+
 OUI:645D86*
  ID_OUI_FROM_DATABASE=Intel Corporate
 
@@ -59588,6 +60134,9 @@ OUI:645D92*
 OUI:645DD7*
  ID_OUI_FROM_DATABASE=Shenzhen Lifesense Medical Electronics Co., Ltd.
 
+OUI:645E2C*
+ ID_OUI_FROM_DATABASE=IRay Technology Co., Ltd.
+
 OUI:645EBE*
  ID_OUI_FROM_DATABASE=Yahoo! JAPAN
 
@@ -59603,6 +60152,51 @@ OUI:646184*
 OUI:646223*
  ID_OUI_FROM_DATABASE=Cellient Co., Ltd.
 
+OUI:6462660*
+ ID_OUI_FROM_DATABASE=MiiVii Dynamics Technology CO.,LTD
+
+OUI:6462661*
+ ID_OUI_FROM_DATABASE=Annapurna labs
+
+OUI:6462662*
+ ID_OUI_FROM_DATABASE=Protectli
+
+OUI:6462663*
+ ID_OUI_FROM_DATABASE=FaceHeart Inc.
+
+OUI:6462664*
+ ID_OUI_FROM_DATABASE=Redstone Systems, Inc.
+
+OUI:6462665*
+ ID_OUI_FROM_DATABASE=Bühler AG
+
+OUI:6462666*
+ ID_OUI_FROM_DATABASE=Pass & Seymour, Inc d/b/a Legrand
+
+OUI:6462667*
+ ID_OUI_FROM_DATABASE=Shenzhen C & D Electronics Co., Ltd.
+
+OUI:6462668*
+ ID_OUI_FROM_DATABASE=Leontech Limited
+
+OUI:6462669*
+ ID_OUI_FROM_DATABASE=Chunghwa System Integration Co., Ltd.
+
+OUI:646266A*
+ ID_OUI_FROM_DATABASE=Sensoro Co., Ltd.
+
+OUI:646266B*
+ ID_OUI_FROM_DATABASE=Signal Hound
+
+OUI:646266C*
+ ID_OUI_FROM_DATABASE=Jiangsu Aisida Electronic Co.,Ltd
+
+OUI:646266D*
+ ID_OUI_FROM_DATABASE=Kobol Innovations Pte. Ltd.
+
+OUI:646266E*
+ ID_OUI_FROM_DATABASE=Shenzhen Jie Shi Lian Industrial Co., LTD
+
 OUI:64628A*
  ID_OUI_FROM_DATABASE=evon GmbH
 
@@ -59648,6 +60242,9 @@ OUI:646E69*
 OUI:646E6C*
  ID_OUI_FROM_DATABASE=Radio Datacom LLC
 
+OUI:646E97*
+ ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
+
 OUI:646EEA*
  ID_OUI_FROM_DATABASE=Iskratel d.o.o.
 
@@ -59804,6 +60401,9 @@ OUI:64A7DD*
 OUI:64A837*
  ID_OUI_FROM_DATABASE=Juni Korea Co., Ltd
 
+OUI:64A965*
+ ID_OUI_FROM_DATABASE=Linkflow Co., Ltd.
+
 OUI:64AE0C*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
@@ -59849,6 +60449,9 @@ OUI:64BC0C*
 OUI:64BC11*
  ID_OUI_FROM_DATABASE=CombiQ AB
 
+OUI:64BC58*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
 OUI:64C2DE*
  ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
 
@@ -59948,6 +60551,9 @@ OUI:64DBA0*
 OUI:64DC01*
  ID_OUI_FROM_DATABASE=Static Systems Group PLC
 
+OUI:64DDE9*
+ ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
+
 OUI:64DE1C*
  ID_OUI_FROM_DATABASE=Kingnetic Pte Ltd
 
@@ -59960,6 +60566,9 @@ OUI:64DFE9*
 OUI:64E161*
  ID_OUI_FROM_DATABASE=DEP Corp.
 
+OUI:64E172*
+ ID_OUI_FROM_DATABASE=Shenzhen Qihoo Intelligent Technology Co.,Ltd
+
 OUI:64E599*
  ID_OUI_FROM_DATABASE=EFM Networks
 
@@ -59972,6 +60581,9 @@ OUI:64E682*
 OUI:64E84F*
  ID_OUI_FROM_DATABASE=Serialway Communication Technology Co. Ltd
 
+OUI:64E881*
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
+
 OUI:64E892*
  ID_OUI_FROM_DATABASE=Morio Denki Co., Ltd.
 
@@ -59999,12 +60611,18 @@ OUI:64EEB7*
 OUI:64F242*
  ID_OUI_FROM_DATABASE=Gerdes Aktiengesellschaft
 
+OUI:64F2FB*
+ ID_OUI_FROM_DATABASE=Hangzhou Ezviz Software Co.,Ltd.
+
 OUI:64F50E*
  ID_OUI_FROM_DATABASE=Kinion Technology Company Limited
 
 OUI:64F69D*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
+OUI:64F6BB*
+ ID_OUI_FROM_DATABASE=Fibocom Wireless Inc.
+
 OUI:64F6F7*
  ID_OUI_FROM_DATABASE=Anhui Dynamic Power Co., Ltd.
 
@@ -60191,6 +60809,9 @@ OUI:682DDC*
 OUI:6831FE*
  ID_OUI_FROM_DATABASE=Teladin Co.,Ltd.
 
+OUI:68332C*
+ ID_OUI_FROM_DATABASE=KENSTEL NETWORKS LIMITED
+
 OUI:683489*
  ID_OUI_FROM_DATABASE=LEA Professional
 
@@ -60245,6 +60866,12 @@ OUI:684749*
 OUI:684898*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:6849B2*
+ ID_OUI_FROM_DATABASE=CARLO GAVAZZI LTD
+
+OUI:684A76*
+ ID_OUI_FROM_DATABASE=eero inc.
+
 OUI:684AAE*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -60317,6 +60944,9 @@ OUI:68692E*
 OUI:686975*
  ID_OUI_FROM_DATABASE=Angler Labs Inc
 
+OUI:6869CA*
+ ID_OUI_FROM_DATABASE=Hitachi, Ltd.
+
 OUI:6869F2*
  ID_OUI_FROM_DATABASE=ComAp s.r.o.
 
@@ -60413,6 +61043,9 @@ OUI:688F2E*
 OUI:688F84*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
+OUI:688FC9*
+ ID_OUI_FROM_DATABASE=Zhuolian (Shenzhen) Communication Co., Ltd
+
 OUI:6891D00*
  ID_OUI_FROM_DATABASE=Central Railway Manufacturing
 
@@ -60578,6 +61211,9 @@ OUI:68B8D9*
 OUI:68B983*
  ID_OUI_FROM_DATABASE=b-plus GmbH
 
+OUI:68B9D3*
+ ID_OUI_FROM_DATABASE=Shenzhen Trolink Technology CO, LTD
+
 OUI:68BC0C*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
@@ -60626,6 +61262,9 @@ OUI:68D247*
 OUI:68D482*
  ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT
 
+OUI:68D79A*
+ ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc.
+
 OUI:68D925*
  ID_OUI_FROM_DATABASE=ProSys Development Services
 
@@ -60659,6 +61298,9 @@ OUI:68DFDD*
 OUI:68E166*
  ID_OUI_FROM_DATABASE=Private
 
+OUI:68E209*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:68E41F*
  ID_OUI_FROM_DATABASE=Unglaube Identech GmbH
 
@@ -60755,6 +61397,9 @@ OUI:6C09D6*
 OUI:6C0B84*
  ID_OUI_FROM_DATABASE=Universal Global Scientific Industrial Co., Ltd.
 
+OUI:6C0D34*
+ ID_OUI_FROM_DATABASE=Nokia
+
 OUI:6C0E0D*
  ID_OUI_FROM_DATABASE=Sony Mobile Communications Inc
 
@@ -60773,6 +61418,9 @@ OUI:6C15F9*
 OUI:6C160E*
  ID_OUI_FROM_DATABASE=ShotTracker
 
+OUI:6C1632*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:6C1811*
  ID_OUI_FROM_DATABASE=Decatur Electronics
 
@@ -60851,6 +61499,9 @@ OUI:6C2E85*
 OUI:6C2F2C*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:6C2F8A*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:6C310E*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
@@ -60917,6 +61568,9 @@ OUI:6C49C1*
 OUI:6C4A39*
  ID_OUI_FROM_DATABASE=BITA
 
+OUI:6C4A85*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
 OUI:6C4B7F*
  ID_OUI_FROM_DATABASE=Vossloh-Schwabe Deutschland GmbH
 
@@ -61031,6 +61685,9 @@ OUI:6C60EB*
 OUI:6C6126*
  ID_OUI_FROM_DATABASE=Rinicom Holdings
 
+OUI:6C61F4*
+ ID_OUI_FROM_DATABASE=SFR
+
 OUI:6C626D*
  ID_OUI_FROM_DATABASE=Micro-Star INT'L CO., LTD
 
@@ -61040,9 +61697,15 @@ OUI:6C639C*
 OUI:6C641A*
  ID_OUI_FROM_DATABASE=Penguin Computing
 
+OUI:6C6A77*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
 OUI:6C6CD3*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
+OUI:6C6D09*
+ ID_OUI_FROM_DATABASE=Kyowa Electronics Co.,Ltd.
+
 OUI:6C6EFE*
  ID_OUI_FROM_DATABASE=Core Logic Inc.
 
@@ -61307,12 +61970,18 @@ OUI:6CD68A*
 OUI:6CD71F*
  ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
 
+OUI:6CD94C*
+ ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
+
 OUI:6CDC6A*
  ID_OUI_FROM_DATABASE=Promethean Limited
 
 OUI:6CDD30*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
+OUI:6CDDBC*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:6CDFFB0*
  ID_OUI_FROM_DATABASE=Shenzhen HDCVT Technology
 
@@ -61424,6 +62093,9 @@ OUI:6CF37F*
 OUI:6CF5E8*
  ID_OUI_FROM_DATABASE=Mooredoll Inc.
 
+OUI:6CF712*
+ ID_OUI_FROM_DATABASE=Nokia
+
 OUI:6CF97C*
  ID_OUI_FROM_DATABASE=Nanoptix Inc.
 
@@ -61457,6 +62129,9 @@ OUI:700258*
 OUI:70037E*
  ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
 
+OUI:70039F*
+ ID_OUI_FROM_DATABASE=Espressif Inc.
+
 OUI:700433*
  ID_OUI_FROM_DATABASE=California Things Inc.
 
@@ -61538,6 +62213,9 @@ OUI:701DC4*
 OUI:701E68*
  ID_OUI_FROM_DATABASE=Hanna Instruments, Inc.
 
+OUI:701F3C*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:701F53*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
@@ -61617,7 +62295,7 @@ OUI:703509*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
 OUI:703811*
- ID_OUI_FROM_DATABASE=Invensys Rail
+ ID_OUI_FROM_DATABASE=Siemens Mobility Limited
 
 OUI:7038B4*
  ID_OUI_FROM_DATABASE=Low Tech Solutions
@@ -61673,6 +62351,9 @@ OUI:70480F*
 OUI:7048F7*
  ID_OUI_FROM_DATABASE=Nintendo Co.,Ltd
 
+OUI:704A0E*
+ ID_OUI_FROM_DATABASE=AMPAK Technology,Inc.
+
 OUI:704AAE*
  ID_OUI_FROM_DATABASE=Xstream Flow (Pty) Ltd
 
@@ -61778,6 +62459,9 @@ OUI:7060DE*
 OUI:706173*
  ID_OUI_FROM_DATABASE=Calantec GmbH
 
+OUI:70617B*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
 OUI:7062B8*
  ID_OUI_FROM_DATABASE=D-Link International
 
@@ -61793,6 +62477,9 @@ OUI:7065A3*
 OUI:70661B*
  ID_OUI_FROM_DATABASE=Sonova AG
 
+OUI:706655*
+ ID_OUI_FROM_DATABASE=AzureWave Technology Inc.
+
 OUI:706879*
  ID_OUI_FROM_DATABASE=Saijo Denki International Co., Ltd.
 
@@ -61845,7 +62532,7 @@ OUI:707630*
  ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
 
 OUI:7076DD*
- ID_OUI_FROM_DATABASE=Oxyguard International A/S
+ ID_OUI_FROM_DATABASE=OxyGuard Internation A/S
 
 OUI:7076F0*
  ID_OUI_FROM_DATABASE=LevelOne Communications (India) Private Limited
@@ -61964,9 +62651,15 @@ OUI:708B78*
 OUI:708BCD*
  ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC.
 
+OUI:708CB6*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:708D09*
  ID_OUI_FROM_DATABASE=Nokia Corporation
 
+OUI:708F47*
+ ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
+
 OUI:70918F*
  ID_OUI_FROM_DATABASE=Weber-Stephen Products LLC
 
@@ -62174,6 +62867,9 @@ OUI:70B3D5029*
 OUI:70B3D502A*
  ID_OUI_FROM_DATABASE=BAE Systems Surface Ships Limited
 
+OUI:70B3D502B*
+ ID_OUI_FROM_DATABASE=Scorpion Precision Industry (HK)CO. Ltd.
+
 OUI:70B3D502D*
  ID_OUI_FROM_DATABASE=NEXTtec srl
 
@@ -62204,6 +62900,9 @@ OUI:70B3D5035*
 OUI:70B3D5037*
  ID_OUI_FROM_DATABASE=EIFFAGE ENERGIE ELECTRONIQUE
 
+OUI:70B3D5038*
+ ID_OUI_FROM_DATABASE=DONG IL VISION Co., Ltd.
+
 OUI:70B3D5039*
  ID_OUI_FROM_DATABASE=DoWoo Digitech
 
@@ -62246,6 +62945,9 @@ OUI:70B3D5045*
 OUI:70B3D5046*
  ID_OUI_FROM_DATABASE=Shenzhen Rihuida Electronics Co,. Ltd
 
+OUI:70B3D5047*
+ ID_OUI_FROM_DATABASE=OOO ORION-R
+
 OUI:70B3D5048*
  ID_OUI_FROM_DATABASE=AvMap srlu
 
@@ -62345,6 +63047,9 @@ OUI:70B3D506B*
 OUI:70B3D506C*
  ID_OUI_FROM_DATABASE=AppTek
 
+OUI:70B3D506D*
+ ID_OUI_FROM_DATABASE=Panoramic Power
+
 OUI:70B3D506E*
  ID_OUI_FROM_DATABASE=GLOBAL-KING INTERNATIONAL CO., LTD.
 
@@ -62654,6 +63359,9 @@ OUI:70B3D50D9*
 OUI:70B3D50DA*
  ID_OUI_FROM_DATABASE=Aquavision Distribution Ltd
 
+OUI:70B3D50DB*
+ ID_OUI_FROM_DATABASE=Cryptotronix LLC
+
 OUI:70B3D50DC*
  ID_OUI_FROM_DATABASE=Talleres de Escoriaza
 
@@ -62717,6 +63425,9 @@ OUI:70B3D50F2*
 OUI:70B3D50F3*
  ID_OUI_FROM_DATABASE=MonsoonRF, Inc.
 
+OUI:70B3D50F4*
+ ID_OUI_FROM_DATABASE=Visual Robotics
+
 OUI:70B3D50F6*
  ID_OUI_FROM_DATABASE=KSE GmbH
 
@@ -62801,6 +63512,9 @@ OUI:70B3D5114*
 OUI:70B3D5115*
  ID_OUI_FROM_DATABASE=Welltec Corp.
 
+OUI:70B3D5117*
+ ID_OUI_FROM_DATABASE=SysCom Automationstechnik GmbH
+
 OUI:70B3D5119*
  ID_OUI_FROM_DATABASE=Private
 
@@ -62864,6 +63578,9 @@ OUI:70B3D512E*
 OUI:70B3D512F*
  ID_OUI_FROM_DATABASE=DSP4YOU LTd
 
+OUI:70B3D5130*
+ ID_OUI_FROM_DATABASE=MG s.r.l.
+
 OUI:70B3D5131*
  ID_OUI_FROM_DATABASE=Inova Design Solutions Ltd
 
@@ -62978,6 +63695,9 @@ OUI:70B3D5158*
 OUI:70B3D5159*
  ID_OUI_FROM_DATABASE=RCH Vietnam Limited Liability Company
 
+OUI:70B3D515A*
+ ID_OUI_FROM_DATABASE=ENABLER LTD.
+
 OUI:70B3D515B*
  ID_OUI_FROM_DATABASE=Armstrong International, Inc.
 
@@ -63101,6 +63821,9 @@ OUI:70B3D5188*
 OUI:70B3D5189*
  ID_OUI_FROM_DATABASE=DAVE SRL
 
+OUI:70B3D518A*
+ ID_OUI_FROM_DATABASE=NSP Europe Ltd
+
 OUI:70B3D518B*
  ID_OUI_FROM_DATABASE=Aplex Technology Inc.
 
@@ -63113,12 +63836,18 @@ OUI:70B3D518D*
 OUI:70B3D518E*
  ID_OUI_FROM_DATABASE=NIPPON SEIKI CO., LTD.
 
+OUI:70B3D518F*
+ ID_OUI_FROM_DATABASE=Newtec A/S
+
 OUI:70B3D5190*
  ID_OUI_FROM_DATABASE=Fantom Wireless, Inc.
 
 OUI:70B3D5192*
  ID_OUI_FROM_DATABASE=ASPT, INC.
 
+OUI:70B3D5193*
+ ID_OUI_FROM_DATABASE=ERA TOYS LIMITED
+
 OUI:70B3D5194*
  ID_OUI_FROM_DATABASE=Husty M.Styczen J.Hupert Sp.J.
 
@@ -63161,6 +63890,9 @@ OUI:70B3D51A5*
 OUI:70B3D51A6*
  ID_OUI_FROM_DATABASE=Robotelf Technologies (Chengdu) Co., Ltd.
 
+OUI:70B3D51A7*
+ ID_OUI_FROM_DATABASE=Elk Solutions, LLC
+
 OUI:70B3D51A8*
  ID_OUI_FROM_DATABASE=STC Rainbow Ltd.
 
@@ -63212,12 +63944,18 @@ OUI:70B3D51BD*
 OUI:70B3D51BE*
  ID_OUI_FROM_DATABASE=Potter Electric Signal Co. LLC
 
+OUI:70B3D51BF*
+ ID_OUI_FROM_DATABASE=DEUTA-WERKE GmbH
+
 OUI:70B3D51C0*
  ID_OUI_FROM_DATABASE=W. H. Leary Co., Inc.
 
 OUI:70B3D51C2*
  ID_OUI_FROM_DATABASE=CENSIS, Uiversity of Glasgow
 
+OUI:70B3D51C3*
+ ID_OUI_FROM_DATABASE=Shanghai Tiancheng Communication Technology Corporation
+
 OUI:70B3D51C4*
  ID_OUI_FROM_DATABASE=Smeg S.p.A.
 
@@ -63245,6 +63983,9 @@ OUI:70B3D51CD*
 OUI:70B3D51CE*
  ID_OUI_FROM_DATABASE=Clear Flow by Antiference
 
+OUI:70B3D51CF*
+ ID_OUI_FROM_DATABASE=Dalcnet srl
+
 OUI:70B3D51D0*
  ID_OUI_FROM_DATABASE=Shenzhen INVT Electric Co.,Ltd
 
@@ -63260,6 +64001,9 @@ OUI:70B3D51D3*
 OUI:70B3D51D4*
  ID_OUI_FROM_DATABASE=Brinkmann Audio GmbH
 
+OUI:70B3D51D5*
+ ID_OUI_FROM_DATABASE=MIVO Technology AB
+
 OUI:70B3D51D6*
  ID_OUI_FROM_DATABASE=MacGray Services
 
@@ -63296,6 +64040,9 @@ OUI:70B3D51E0*
 OUI:70B3D51E1*
  ID_OUI_FROM_DATABASE=TEX COMPUTER SRL
 
+OUI:70B3D51E2*
+ ID_OUI_FROM_DATABASE=Shenzhen CAMERAY ELECTRONIC CO., LTD
+
 OUI:70B3D51E3*
  ID_OUI_FROM_DATABASE=Hatel Elektronik LTD. STI.
 
@@ -63308,6 +64055,9 @@ OUI:70B3D51E5*
 OUI:70B3D51E6*
  ID_OUI_FROM_DATABASE=Sanmina Israel
 
+OUI:70B3D51E7*
+ ID_OUI_FROM_DATABASE=DogWatch Inc
+
 OUI:70B3D51E8*
  ID_OUI_FROM_DATABASE=Gogo BA
 
@@ -63332,6 +64082,9 @@ OUI:70B3D51F0*
 OUI:70B3D51F1*
  ID_OUI_FROM_DATABASE=DIEHL Connectivity Solutions
 
+OUI:70B3D51F2*
+ ID_OUI_FROM_DATABASE=YUYAMA MFG Co.,Ltd
+
 OUI:70B3D51F3*
  ID_OUI_FROM_DATABASE=Smart Energy Code Company Limited
 
@@ -63548,6 +64301,9 @@ OUI:70B3D5241*
 OUI:70B3D5243*
  ID_OUI_FROM_DATABASE=Rohde&Schwarz Topex SA
 
+OUI:70B3D5244*
+ ID_OUI_FROM_DATABASE=DAT Informatics Pvt Ltd
+
 OUI:70B3D5245*
  ID_OUI_FROM_DATABASE=Newtec A/S
 
@@ -63557,6 +64313,9 @@ OUI:70B3D5246*
 OUI:70B3D5248*
  ID_OUI_FROM_DATABASE=GL TECH CO.,LTD
 
+OUI:70B3D5249*
+ ID_OUI_FROM_DATABASE=Kospel S.A.
+
 OUI:70B3D524A*
  ID_OUI_FROM_DATABASE=Unmukti Technology Pvt Ltd
 
@@ -63608,6 +64367,9 @@ OUI:70B3D525A*
 OUI:70B3D525B*
  ID_OUI_FROM_DATABASE=GID Industrial
 
+OUI:70B3D525C*
+ ID_OUI_FROM_DATABASE=ARCLAN'SYSTEM
+
 OUI:70B3D525D*
  ID_OUI_FROM_DATABASE=Mimo Networks
 
@@ -63704,6 +64466,9 @@ OUI:70B3D5280*
 OUI:70B3D5281*
  ID_OUI_FROM_DATABASE=ITG.CO.LTD
 
+OUI:70B3D5282*
+ ID_OUI_FROM_DATABASE=SAMBO HITECH
+
 OUI:70B3D5283*
  ID_OUI_FROM_DATABASE=TextNinja Co.
 
@@ -63866,6 +64631,9 @@ OUI:70B3D52BF*
 OUI:70B3D52C0*
  ID_OUI_FROM_DATABASE=Sensative AB
 
+OUI:70B3D52C1*
+ ID_OUI_FROM_DATABASE=Avlinkpro
+
 OUI:70B3D52C2*
  ID_OUI_FROM_DATABASE=Quantum Detectors
 
@@ -64118,18 +64886,27 @@ OUI:70B3D532C*
 OUI:70B3D532D*
  ID_OUI_FROM_DATABASE=Hanwell Technology Co., Ltd.
 
+OUI:70B3D532E*
+ ID_OUI_FROM_DATABASE=A&T Corporation
+
 OUI:70B3D532F*
  ID_OUI_FROM_DATABASE=Movidius SRL
 
 OUI:70B3D5330*
  ID_OUI_FROM_DATABASE=iOne
 
+OUI:70B3D5331*
+ ID_OUI_FROM_DATABASE=Firecom, Inc.
+
 OUI:70B3D5332*
  ID_OUI_FROM_DATABASE=InnoSenT
 
 OUI:70B3D5334*
  ID_OUI_FROM_DATABASE=Dokuen Co. Ltd.
 
+OUI:70B3D5335*
+ ID_OUI_FROM_DATABASE=Jonsa Australia Pty Ltd
+
 OUI:70B3D5336*
  ID_OUI_FROM_DATABASE=Synaccess Networks Inc.
 
@@ -64271,6 +65048,9 @@ OUI:70B3D5367*
 OUI:70B3D5368*
  ID_OUI_FROM_DATABASE=White Matter LLC
 
+OUI:70B3D5369*
+ ID_OUI_FROM_DATABASE=ALVAT s.r.o.
+
 OUI:70B3D536A*
  ID_OUI_FROM_DATABASE=Becton Dickinson
 
@@ -64343,6 +65123,9 @@ OUI:70B3D5383*
 OUI:70B3D5384*
  ID_OUI_FROM_DATABASE=Sensohive Technologies
 
+OUI:70B3D5385*
+ ID_OUI_FROM_DATABASE=Kamacho Scale Co., Ltd.
+
 OUI:70B3D5387*
  ID_OUI_FROM_DATABASE=GWF MessSysteme AG
 
@@ -64469,6 +65252,9 @@ OUI:70B3D53BF*
 OUI:70B3D53C0*
  ID_OUI_FROM_DATABASE=DK-Technologies A/S
 
+OUI:70B3D53C1*
+ ID_OUI_FROM_DATABASE=thingdust AG
+
 OUI:70B3D53C2*
  ID_OUI_FROM_DATABASE=Cellular Specialties, Inc.
 
@@ -64517,6 +65303,9 @@ OUI:70B3D53D4*
 OUI:70B3D53D5*
  ID_OUI_FROM_DATABASE=oxynet Solutions
 
+OUI:70B3D53D6*
+ ID_OUI_FROM_DATABASE=Ariston Thermo s.p.a.
+
 OUI:70B3D53D7*
  ID_OUI_FROM_DATABASE=Remote Sensing Solutions, Inc.
 
@@ -64541,6 +65330,9 @@ OUI:70B3D53DE*
 OUI:70B3D53DF*
  ID_OUI_FROM_DATABASE=MultiDyne
 
+OUI:70B3D53E0*
+ ID_OUI_FROM_DATABASE=Gogo Business Aviation
+
 OUI:70B3D53E1*
  ID_OUI_FROM_DATABASE=Barnstormer Softworks
 
@@ -64619,6 +65411,9 @@ OUI:70B3D53FA*
 OUI:70B3D53FB*
  ID_OUI_FROM_DATABASE=Liberty Reach
 
+OUI:70B3D53FC*
+ ID_OUI_FROM_DATABASE=TangRen C&S CO., Ltd
+
 OUI:70B3D53FE*
  ID_OUI_FROM_DATABASE=Mentor Graphics
 
@@ -64661,6 +65456,9 @@ OUI:70B3D540A*
 OUI:70B3D540B*
  ID_OUI_FROM_DATABASE=QUERCUS TECHNOLOGIES, S.L.
 
+OUI:70B3D540D*
+ ID_OUI_FROM_DATABASE=Grupo Epelsa S.L.
+
 OUI:70B3D540E*
  ID_OUI_FROM_DATABASE=Liaoyun Information Technology Co., Ltd.
 
@@ -64817,6 +65615,9 @@ OUI:70B3D5449*
 OUI:70B3D544B*
  ID_OUI_FROM_DATABASE=Open System Solutions Limited
 
+OUI:70B3D544D*
+ ID_OUI_FROM_DATABASE=Vessel Technology Ltd
+
 OUI:70B3D544E*
  ID_OUI_FROM_DATABASE=Solace Systems Inc.
 
@@ -64871,6 +65672,12 @@ OUI:70B3D5463*
 OUI:70B3D5465*
  ID_OUI_FROM_DATABASE=ENERGISME
 
+OUI:70B3D5466*
+ ID_OUI_FROM_DATABASE=SYLink Technologie
+
+OUI:70B3D5467*
+ ID_OUI_FROM_DATABASE=GreenWake Technologies
+
 OUI:70B3D5469*
  ID_OUI_FROM_DATABASE=Gentec Systems  Co.
 
@@ -64895,6 +65702,9 @@ OUI:70B3D5471*
 OUI:70B3D5472*
  ID_OUI_FROM_DATABASE=Quadio Devices Private Limited
 
+OUI:70B3D5473*
+ ID_OUI_FROM_DATABASE=KeyProd
+
 OUI:70B3D5475*
  ID_OUI_FROM_DATABASE=EWATTCH
 
@@ -64928,6 +65738,9 @@ OUI:70B3D547F*
 OUI:70B3D5480*
  ID_OUI_FROM_DATABASE=Emergency Lighting Products Limited
 
+OUI:70B3D5481*
+ ID_OUI_FROM_DATABASE=STEP sarl
+
 OUI:70B3D5482*
  ID_OUI_FROM_DATABASE=Aeryon Labs Inc
 
@@ -65063,6 +65876,9 @@ OUI:70B3D54B3*
 OUI:70B3D54B4*
  ID_OUI_FROM_DATABASE=Hi Tech Systems Ltd
 
+OUI:70B3D54B5*
+ ID_OUI_FROM_DATABASE=Toolplanet Co., Ltd.
+
 OUI:70B3D54B6*
  ID_OUI_FROM_DATABASE=VEILUX INC.
 
@@ -65120,6 +65936,9 @@ OUI:70B3D54C8*
 OUI:70B3D54C9*
  ID_OUI_FROM_DATABASE=Elsist Srl
 
+OUI:70B3D54CA*
+ ID_OUI_FROM_DATABASE=PCB Piezotronics
+
 OUI:70B3D54CC*
  ID_OUI_FROM_DATABASE=FRESENIUS MEDICAL CARE
 
@@ -65138,6 +65957,9 @@ OUI:70B3D54D1*
 OUI:70B3D54D2*
  ID_OUI_FROM_DATABASE=Biotage Sweden AB
 
+OUI:70B3D54D3*
+ ID_OUI_FROM_DATABASE=Hefei STAROT Technology Co.,Ltd
+
 OUI:70B3D54D4*
  ID_OUI_FROM_DATABASE=Nortek Global HVAC
 
@@ -65210,6 +66032,9 @@ OUI:70B3D54F1*
 OUI:70B3D54F2*
  ID_OUI_FROM_DATABASE=COMPAL ELECTRONICS, INC.
 
+OUI:70B3D54F3*
+ ID_OUI_FROM_DATABASE=XPS ELETRONICA LTDA
+
 OUI:70B3D54F4*
  ID_OUI_FROM_DATABASE=WiTagg, Inc
 
@@ -65279,6 +66104,9 @@ OUI:70B3D550D*
 OUI:70B3D550E*
  ID_OUI_FROM_DATABASE=Micro Trend Automation Co., LTD
 
+OUI:70B3D550F*
+ ID_OUI_FROM_DATABASE=LLC Sarov Innovative Technologies (WIZOLUTION)
+
 OUI:70B3D5510*
  ID_OUI_FROM_DATABASE=PSL ELEKTRONİK SANAYİ VE TİCARET A.S.
 
@@ -65345,6 +66173,9 @@ OUI:70B3D5526*
 OUI:70B3D5528*
  ID_OUI_FROM_DATABASE=Aplex Technology Inc.
 
+OUI:70B3D5529*
+ ID_OUI_FROM_DATABASE=Inventeq B.V.
+
 OUI:70B3D552A*
  ID_OUI_FROM_DATABASE=Dataflex International BV
 
@@ -65360,6 +66191,9 @@ OUI:70B3D552D*
 OUI:70B3D552E*
  ID_OUI_FROM_DATABASE=Swissponic Sagl
 
+OUI:70B3D552F*
+ ID_OUI_FROM_DATABASE=R.C. Systems Inc
+
 OUI:70B3D5530*
  ID_OUI_FROM_DATABASE=iSiS-Ex Limited
 
@@ -65385,7 +66219,7 @@ OUI:70B3D5539*
  ID_OUI_FROM_DATABASE=Tempris GmbH
 
 OUI:70B3D553A*
- ID_OUI_FROM_DATABASE=Pano0ramic Power
+ ID_OUI_FROM_DATABASE=Panoramic Power
 
 OUI:70B3D553B*
  ID_OUI_FROM_DATABASE=Mr.Loop
@@ -65396,6 +66230,15 @@ OUI:70B3D553C*
 OUI:70B3D553D*
  ID_OUI_FROM_DATABASE=ACCEL CORP
 
+OUI:70B3D553E*
+ ID_OUI_FROM_DATABASE=Asiga Pty Ltd
+
+OUI:70B3D553F*
+ ID_OUI_FROM_DATABASE=Abbott Diagnostics Technologies AS
+
+OUI:70B3D5541*
+ ID_OUI_FROM_DATABASE=Nanjing Pingguang Electronic Technology Co., Ltd
+
 OUI:70B3D5542*
  ID_OUI_FROM_DATABASE=RTDS Technologies Inc.
 
@@ -65588,6 +66431,9 @@ OUI:70B3D5588*
 OUI:70B3D5589*
  ID_OUI_FROM_DATABASE=Cityntel OU
 
+OUI:70B3D558A*
+ ID_OUI_FROM_DATABASE=ITK Dr. Kassen GmbH
+
 OUI:70B3D558C*
  ID_OUI_FROM_DATABASE=OPTSYS
 
@@ -65672,12 +66518,18 @@ OUI:70B3D55AA*
 OUI:70B3D55AB*
  ID_OUI_FROM_DATABASE=Sea Air and Land Communications Ltd
 
+OUI:70B3D55AC*
+ ID_OUI_FROM_DATABASE=LM-Instruments Oy
+
 OUI:70B3D55AD*
  ID_OUI_FROM_DATABASE=Profotech
 
 OUI:70B3D55AE*
  ID_OUI_FROM_DATABASE=TinTec Co., Ltd.
 
+OUI:70B3D55AF*
+ ID_OUI_FROM_DATABASE=JENG IoT BV
+
 OUI:70B3D55B0*
  ID_OUI_FROM_DATABASE=Qxperts Italia S.r.l.
 
@@ -65723,6 +66575,9 @@ OUI:70B3D55C4*
 OUI:70B3D55C5*
  ID_OUI_FROM_DATABASE=Haag-Streit AG
 
+OUI:70B3D55C6*
+ ID_OUI_FROM_DATABASE=C4I Systems Ltd
+
 OUI:70B3D55C8*
  ID_OUI_FROM_DATABASE=YUYAMA MFG Co.,Ltd
 
@@ -65744,6 +66599,9 @@ OUI:70B3D55D0*
 OUI:70B3D55D1*
  ID_OUI_FROM_DATABASE=Software Motor Corp
 
+OUI:70B3D55D2*
+ ID_OUI_FROM_DATABASE=Contec Americas Inc.
+
 OUI:70B3D55D3*
  ID_OUI_FROM_DATABASE=Supracon AG
 
@@ -65843,6 +66701,9 @@ OUI:70B3D55F4*
 OUI:70B3D55F6*
  ID_OUI_FROM_DATABASE=FreeFlight Systems
 
+OUI:70B3D55F7*
+ ID_OUI_FROM_DATABASE=JFA Electronics Industry and Commerce EIRELI
+
 OUI:70B3D55F8*
  ID_OUI_FROM_DATABASE=Forcite Helmet Systems Pty Ltd
 
@@ -65867,6 +66728,9 @@ OUI:70B3D55FF*
 OUI:70B3D5600*
  ID_OUI_FROM_DATABASE=Stellwerk GmbH
 
+OUI:70B3D5601*
+ ID_OUI_FROM_DATABASE=Tricom Research Inc.
+
 OUI:70B3D5602*
  ID_OUI_FROM_DATABASE=Quantum Opus, LLC
 
@@ -65915,6 +66779,9 @@ OUI:70B3D5611*
 OUI:70B3D5613*
  ID_OUI_FROM_DATABASE=Suprock Technologies
 
+OUI:70B3D5614*
+ ID_OUI_FROM_DATABASE=QUALITTEQ LLC
+
 OUI:70B3D5615*
  ID_OUI_FROM_DATABASE=JSC OTZVUK
 
@@ -65957,12 +66824,21 @@ OUI:70B3D5625*
 OUI:70B3D5628*
  ID_OUI_FROM_DATABASE=MECT SRL
 
+OUI:70B3D562A*
+ ID_OUI_FROM_DATABASE=DOGA
+
 OUI:70B3D562B*
  ID_OUI_FROM_DATABASE=Silicann Systems GmbH
 
 OUI:70B3D562C*
  ID_OUI_FROM_DATABASE=OOO NTC Rotek
 
+OUI:70B3D562D*
+ ID_OUI_FROM_DATABASE=elements
+
+OUI:70B3D562E*
+ ID_OUI_FROM_DATABASE=LINEAGE POWER PVT LTD.,
+
 OUI:70B3D562F*
  ID_OUI_FROM_DATABASE=BARCO, s.r.o.
 
@@ -65990,6 +66866,9 @@ OUI:70B3D5637*
 OUI:70B3D5638*
  ID_OUI_FROM_DATABASE=Parkalot Denmark ApS
 
+OUI:70B3D5639*
+ ID_OUI_FROM_DATABASE=DORLET SAU
+
 OUI:70B3D563A*
  ID_OUI_FROM_DATABASE=DAVE SRL
 
@@ -66011,6 +66890,9 @@ OUI:70B3D563F*
 OUI:70B3D5640*
  ID_OUI_FROM_DATABASE=Electronic Equipment Company Pvt. Ltd.
 
+OUI:70B3D5641*
+ ID_OUI_FROM_DATABASE=Burk Technology
+
 OUI:70B3D5642*
  ID_OUI_FROM_DATABASE=MB connect line GmbH Fernwartungssysteme
 
@@ -66086,6 +66968,9 @@ OUI:70B3D565D*
 OUI:70B3D565E*
  ID_OUI_FROM_DATABASE=Season Electronics Ltd
 
+OUI:70B3D565F*
+ ID_OUI_FROM_DATABASE=Axnes AS
+
 OUI:70B3D5660*
  ID_OUI_FROM_DATABASE=Smart Service Technologies CO., LTD
 
@@ -66108,7 +66993,7 @@ OUI:70B3D5667*
  ID_OUI_FROM_DATABASE=CT Company
 
 OUI:70B3D5669*
- ID_OUI_FROM_DATABASE=Pano0ramic Power
+ ID_OUI_FROM_DATABASE=Panoramic Power
 
 OUI:70B3D566A*
  ID_OUI_FROM_DATABASE=Private
@@ -66173,6 +67058,9 @@ OUI:70B3D5680*
 OUI:70B3D5682*
  ID_OUI_FROM_DATABASE=Rosslare Enterprises Limited
 
+OUI:70B3D5683*
+ ID_OUI_FROM_DATABASE=DECYBEN
+
 OUI:70B3D5684*
  ID_OUI_FROM_DATABASE=LECO Corporation
 
@@ -66221,6 +67109,9 @@ OUI:70B3D5696*
 OUI:70B3D5697*
  ID_OUI_FROM_DATABASE=Alazar Technologies Inc.
 
+OUI:70B3D5699*
+ ID_OUI_FROM_DATABASE=Flextronics International Kft
+
 OUI:70B3D569A*
  ID_OUI_FROM_DATABASE=Altaneos
 
@@ -66311,6 +67202,9 @@ OUI:70B3D56BB*
 OUI:70B3D56BC*
  ID_OUI_FROM_DATABASE=EA Elektroautomatik GmbH & Co. KG
 
+OUI:70B3D56BD*
+ ID_OUI_FROM_DATABASE=RCH Vietnam Limited Liability Company
+
 OUI:70B3D56BE*
  ID_OUI_FROM_DATABASE=VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD
 
@@ -66320,12 +67214,18 @@ OUI:70B3D56BF*
 OUI:70B3D56C1*
  ID_OUI_FROM_DATABASE=Labtronik s.r.l.
 
+OUI:70B3D56C2*
+ ID_OUI_FROM_DATABASE=TEX COMPUTER SRL
+
 OUI:70B3D56C3*
  ID_OUI_FROM_DATABASE=BEIJING ZGH SECURITY RESEARCH INSTITUTE CO., LTD
 
 OUI:70B3D56C5*
  ID_OUI_FROM_DATABASE=CJSC «Russian telecom equipment company» (CJSC RTEC)
 
+OUI:70B3D56C6*
+ ID_OUI_FROM_DATABASE=Abbott Diagnostics Technologies AS
+
 OUI:70B3D56C7*
  ID_OUI_FROM_DATABASE=Becton Dickinson
 
@@ -66359,6 +67259,9 @@ OUI:70B3D56D3*
 OUI:70B3D56D6*
  ID_OUI_FROM_DATABASE=KMtronic Ltd.
 
+OUI:70B3D56D7*
+ ID_OUI_FROM_DATABASE=MB connect line GmbH Fernwartungssysteme
+
 OUI:70B3D56D8*
  ID_OUI_FROM_DATABASE=Shanghai YuanAn Environmental Protection Technology Co.,Ltd
 
@@ -66635,6 +67538,9 @@ OUI:70B3D5742*
 OUI:70B3D5743*
  ID_OUI_FROM_DATABASE=EA Elektroautomatik GmbH & Co. KG
 
+OUI:70B3D5744*
+ ID_OUI_FROM_DATABASE=PHYZHON Health Inc
+
 OUI:70B3D5745*
  ID_OUI_FROM_DATABASE=TMSI LLC
 
@@ -66797,6 +67703,9 @@ OUI:70B3D577D*
 OUI:70B3D577E*
  ID_OUI_FROM_DATABASE=Blue Marble Communications, Inc.
 
+OUI:70B3D577F*
+ ID_OUI_FROM_DATABASE=Microchip Technology Germany II GmbH&Co.KG
+
 OUI:70B3D5780*
  ID_OUI_FROM_DATABASE=NIDEC LEROY-SOMER
 
@@ -66836,6 +67745,9 @@ OUI:70B3D578B*
 OUI:70B3D578C*
  ID_OUI_FROM_DATABASE=Survalent Technology Corporation
 
+OUI:70B3D578D*
+ ID_OUI_FROM_DATABASE=AVL DiTEST GmbH
+
 OUI:70B3D578E*
  ID_OUI_FROM_DATABASE=effectas GmbH
 
@@ -67034,6 +67946,9 @@ OUI:70B3D57D9*
 OUI:70B3D57DA*
  ID_OUI_FROM_DATABASE=Grupo Epelsa S.L.
 
+OUI:70B3D57DB*
+ ID_OUI_FROM_DATABASE=aquila biolabs GmbH
+
 OUI:70B3D57DC*
  ID_OUI_FROM_DATABASE=Software Systems Plus
 
@@ -67118,6 +68033,9 @@ OUI:70B3D57F9*
 OUI:70B3D57FB*
  ID_OUI_FROM_DATABASE=db Broadcast Products Ltd
 
+OUI:70B3D57FC*
+ ID_OUI_FROM_DATABASE=Surion (Pty) Ltd
+
 OUI:70B3D57FD*
  ID_OUI_FROM_DATABASE=SYS TEC electronic GmbH
 
@@ -67130,6 +68048,9 @@ OUI:70B3D57FF*
 OUI:70B3D5800*
  ID_OUI_FROM_DATABASE=HeadsafeIP PTY LTD
 
+OUI:70B3D5801*
+ ID_OUI_FROM_DATABASE=Glory Technology Service Inc.
+
 OUI:70B3D5802*
  ID_OUI_FROM_DATABASE=Qingdao CNR HITACH Railway Signal&communication co.,ltd
 
@@ -67349,6 +68270,9 @@ OUI:70B3D5855*
 OUI:70B3D5857*
  ID_OUI_FROM_DATABASE=RCH ITALIA SPA
 
+OUI:70B3D5858*
+ ID_OUI_FROM_DATABASE=Hubbell Power Systems
+
 OUI:70B3D585A*
  ID_OUI_FROM_DATABASE=BRUSHIES
 
@@ -67388,6 +68312,9 @@ OUI:70B3D5866*
 OUI:70B3D5868*
  ID_OUI_FROM_DATABASE=U-JIN Mesco Co., Ltd.
 
+OUI:70B3D5869*
+ ID_OUI_FROM_DATABASE=chargeBIG
+
 OUI:70B3D586A*
  ID_OUI_FROM_DATABASE=Stealth Communications
 
@@ -67412,6 +68339,9 @@ OUI:70B3D5870*
 OUI:70B3D5871*
  ID_OUI_FROM_DATABASE=Oso Technologies
 
+OUI:70B3D5872*
+ ID_OUI_FROM_DATABASE=Nippon Safety co,ltd
+
 OUI:70B3D5873*
  ID_OUI_FROM_DATABASE=Vishay Nobel AB
 
@@ -67478,6 +68408,9 @@ OUI:70B3D588B*
 OUI:70B3D588D*
  ID_OUI_FROM_DATABASE=LG Electronics
 
+OUI:70B3D588E*
+ ID_OUI_FROM_DATABASE=RCH Vietnam Limited Liability Company
+
 OUI:70B3D588F*
  ID_OUI_FROM_DATABASE=Quaesta Instruments, LLC
 
@@ -67505,6 +68438,9 @@ OUI:70B3D5896*
 OUI:70B3D5897*
  ID_OUI_FROM_DATABASE=EFG CZ spol. s r.o.
 
+OUI:70B3D5898*
+ ID_OUI_FROM_DATABASE=Salupo Sas
+
 OUI:70B3D5899*
  ID_OUI_FROM_DATABASE=Viotec USA
 
@@ -67538,6 +68474,9 @@ OUI:70B3D58A5*
 OUI:70B3D58A6*
  ID_OUI_FROM_DATABASE=CRDE
 
+OUI:70B3D58A7*
+ ID_OUI_FROM_DATABASE=Tucsen Photonics Co., Ltd.
+
 OUI:70B3D58A8*
  ID_OUI_FROM_DATABASE=megatec electronic GmbH
 
@@ -67691,6 +68630,9 @@ OUI:70B3D58E4*
 OUI:70B3D58E6*
  ID_OUI_FROM_DATABASE=Mothonic AB
 
+OUI:70B3D58E7*
+ ID_OUI_FROM_DATABASE=REO AG
+
 OUI:70B3D58EA*
  ID_OUI_FROM_DATABASE=JLCooper Electronics
 
@@ -67820,6 +68762,9 @@ OUI:70B3D591F*
 OUI:70B3D5920*
  ID_OUI_FROM_DATABASE=SLAT
 
+OUI:70B3D5922*
+ ID_OUI_FROM_DATABASE=Adcole Maryland Aerospace
+
 OUI:70B3D5923*
  ID_OUI_FROM_DATABASE=eumig industrie-tv GmbH
 
@@ -67907,6 +68852,9 @@ OUI:70B3D5942*
 OUI:70B3D5943*
  ID_OUI_FROM_DATABASE=Abbott Medical Optics Inc.
 
+OUI:70B3D5944*
+ ID_OUI_FROM_DATABASE=Chromateq
+
 OUI:70B3D5945*
  ID_OUI_FROM_DATABASE=Symboticware Incorporated
 
@@ -67976,6 +68924,9 @@ OUI:70B3D595B*
 OUI:70B3D595C*
  ID_OUI_FROM_DATABASE=Wilson Electronics
 
+OUI:70B3D595D*
+ ID_OUI_FROM_DATABASE=GIORDANO CONTROLS SPA
+
 OUI:70B3D595E*
  ID_OUI_FROM_DATABASE=BLOCKSI LLC
 
@@ -68045,6 +68996,9 @@ OUI:70B3D5977*
 OUI:70B3D5978*
  ID_OUI_FROM_DATABASE=Satixfy Israel Ltd.
 
+OUI:70B3D5979*
+ ID_OUI_FROM_DATABASE=eSMART Technologies SA
+
 OUI:70B3D597A*
  ID_OUI_FROM_DATABASE=Orion Corporation
 
@@ -68054,12 +69008,18 @@ OUI:70B3D597B*
 OUI:70B3D597C*
  ID_OUI_FROM_DATABASE=Nu-Tek Power Controls and Automation
 
+OUI:70B3D597D*
+ ID_OUI_FROM_DATABASE=RCH Vietnam Limited Liability Company
+
 OUI:70B3D597E*
  ID_OUI_FROM_DATABASE=Public Joint Stock Company Morion
 
 OUI:70B3D597F*
  ID_OUI_FROM_DATABASE=BISTOS.,Co.,Ltd
 
+OUI:70B3D5980*
+ ID_OUI_FROM_DATABASE=Beijing Yourong Runda Rechnology Development Co.Ltd.
+
 OUI:70B3D5981*
  ID_OUI_FROM_DATABASE=Zamir Recognition Systems Ltd.
 
@@ -68414,6 +69374,9 @@ OUI:70B3D5A07*
 OUI:70B3D5A08*
  ID_OUI_FROM_DATABASE=BioBusiness
 
+OUI:70B3D5A09*
+ ID_OUI_FROM_DATABASE=Smart Embedded Systems
+
 OUI:70B3D5A0A*
  ID_OUI_FROM_DATABASE=CAPSYS
 
@@ -68666,6 +69629,9 @@ OUI:70B3D5A68*
 OUI:70B3D5A69*
  ID_OUI_FROM_DATABASE=Leviathan Solutions Ltd.
 
+OUI:70B3D5A6A*
+ ID_OUI_FROM_DATABASE=Privafy, Inc
+
 OUI:70B3D5A6B*
  ID_OUI_FROM_DATABASE=xmi systems
 
@@ -68697,11 +69663,14 @@ OUI:70B3D5A74*
  ID_OUI_FROM_DATABASE=Sadel S.p.A.
 
 OUI:70B3D5A75*
- ID_OUI_FROM_DATABASE=Taejin InforTech
+ ID_OUI_FROM_DATABASE=Taejin InfoTech
 
 OUI:70B3D5A76*
  ID_OUI_FROM_DATABASE=Pietro Fiorentini
 
+OUI:70B3D5A77*
+ ID_OUI_FROM_DATABASE=SPX Radiodetection
+
 OUI:70B3D5A78*
  ID_OUI_FROM_DATABASE=Bionics co.,ltd.
 
@@ -68720,6 +69689,9 @@ OUI:70B3D5A7D*
 OUI:70B3D5A7E*
  ID_OUI_FROM_DATABASE=QUICCO SOUND Corporation
 
+OUI:70B3D5A7F*
+ ID_OUI_FROM_DATABASE=AUDIO VISUAL DIGITAL SYSTEMS
+
 OUI:70B3D5A80*
  ID_OUI_FROM_DATABASE=EVCO SPA
 
@@ -68729,6 +69701,9 @@ OUI:70B3D5A81*
 OUI:70B3D5A82*
  ID_OUI_FROM_DATABASE=Telefrank GmbH
 
+OUI:70B3D5A83*
+ ID_OUI_FROM_DATABASE=SHENZHEN HUINENGYUAN Technology Co., Ltd
+
 OUI:70B3D5A84*
  ID_OUI_FROM_DATABASE=SOREL GmbH Mikroelektronik
 
@@ -68945,6 +69920,12 @@ OUI:70B3D5AD1*
 OUI:70B3D5AD2*
  ID_OUI_FROM_DATABASE=Wart-Elektronik
 
+OUI:70B3D5AD3*
+ ID_OUI_FROM_DATABASE=WARECUBE,INC
+
+OUI:70B3D5AD4*
+ ID_OUI_FROM_DATABASE=INVISSYS
+
 OUI:70B3D5AD5*
  ID_OUI_FROM_DATABASE=Birdland Audio
 
@@ -69062,6 +70043,9 @@ OUI:70B3D5B00*
 OUI:70B3D5B02*
  ID_OUI_FROM_DATABASE=Nordic Automation Systems AS
 
+OUI:70B3D5B03*
+ ID_OUI_FROM_DATABASE=Sprintshield d.o.o.
+
 OUI:70B3D5B04*
  ID_OUI_FROM_DATABASE=Herrmann Datensysteme GmbH
 
@@ -69095,9 +70079,6 @@ OUI:70B3D5B10*
 OUI:70B3D5B11*
  ID_OUI_FROM_DATABASE=CAB S.R.L.
 
-OUI:70B3D5B12*
- ID_OUI_FROM_DATABASE=SFR
-
 OUI:70B3D5B13*
  ID_OUI_FROM_DATABASE=Omwave
 
@@ -69119,6 +70100,9 @@ OUI:70B3D5B19*
 OUI:70B3D5B1A*
  ID_OUI_FROM_DATABASE=Aaronia AG
 
+OUI:70B3D5B1B*
+ ID_OUI_FROM_DATABASE=Technology Link Corporation
+
 OUI:70B3D5B1D*
  ID_OUI_FROM_DATABASE=Safelet BV
 
@@ -69182,6 +70166,9 @@ OUI:70B3D5B34*
 OUI:70B3D5B35*
  ID_OUI_FROM_DATABASE=Rexxam Co.,Ltd.
 
+OUI:70B3D5B36*
+ ID_OUI_FROM_DATABASE=Cetitec GmbH
+
 OUI:70B3D5B37*
  ID_OUI_FROM_DATABASE=CODEC Co., Ltd.
 
@@ -69212,6 +70199,9 @@ OUI:70B3D5B3F*
 OUI:70B3D5B40*
  ID_OUI_FROM_DATABASE=Wuhan Xingtuxinke ELectronic Co.,Ltd
 
+OUI:70B3D5B41*
+ ID_OUI_FROM_DATABASE=T&M Media Pty Ltd
+
 OUI:70B3D5B43*
  ID_OUI_FROM_DATABASE=ZAO ZEO
 
@@ -69326,6 +70316,9 @@ OUI:70B3D5B73*
 OUI:70B3D5B74*
  ID_OUI_FROM_DATABASE=OnYield Inc Ltd
 
+OUI:70B3D5B75*
+ ID_OUI_FROM_DATABASE=Grossenbacher Systeme AG
+
 OUI:70B3D5B76*
  ID_OUI_FROM_DATABASE=ATL-SD
 
@@ -69401,6 +70394,9 @@ OUI:70B3D5B93*
 OUI:70B3D5B94*
  ID_OUI_FROM_DATABASE=Cygnetic Technologies (Pty) Ltd
 
+OUI:70B3D5B96*
+ ID_OUI_FROM_DATABASE=Oculii
+
 OUI:70B3D5B97*
  ID_OUI_FROM_DATABASE=Canam Technology, Inc.
 
@@ -69701,6 +70697,9 @@ OUI:70B3D5C07*
 OUI:70B3D5C08*
  ID_OUI_FROM_DATABASE=Talleres de Escoriaza SA
 
+OUI:70B3D5C09*
+ ID_OUI_FROM_DATABASE=RCH Vietnam Limited Liability Company
+
 OUI:70B3D5C0A*
  ID_OUI_FROM_DATABASE=Infosocket Co., Ltd.
 
@@ -69737,6 +70736,9 @@ OUI:70B3D5C16*
 OUI:70B3D5C17*
  ID_OUI_FROM_DATABASE=Potter Electric Signal Co. LLC
 
+OUI:70B3D5C18*
+ ID_OUI_FROM_DATABASE=Sanmina Israel
+
 OUI:70B3D5C1A*
  ID_OUI_FROM_DATABASE=Xylon
 
@@ -69779,6 +70781,9 @@ OUI:70B3D5C29*
 OUI:70B3D5C2A*
  ID_OUI_FROM_DATABASE=Array Telepresence
 
+OUI:70B3D5C2B*
+ ID_OUI_FROM_DATABASE=YUYAMA MFG Co.,Ltd
+
 OUI:70B3D5C2C*
  ID_OUI_FROM_DATABASE=Dromont S.p.A.
 
@@ -69791,6 +70796,9 @@ OUI:70B3D5C2E*
 OUI:70B3D5C2F*
  ID_OUI_FROM_DATABASE=ATBiS Co.,Ltd
 
+OUI:70B3D5C31*
+ ID_OUI_FROM_DATABASE=German Power GmbH
+
 OUI:70B3D5C32*
  ID_OUI_FROM_DATABASE=INFRASAFE/ ADVANTOR SYSTEMS
 
@@ -69803,6 +70811,9 @@ OUI:70B3D5C34*
 OUI:70B3D5C35*
  ID_OUI_FROM_DATABASE=Vibrationmaster
 
+OUI:70B3D5C36*
+ ID_OUI_FROM_DATABASE=Knowledge Resources GmbH
+
 OUI:70B3D5C37*
  ID_OUI_FROM_DATABASE=Keycom Corp.
 
@@ -69875,6 +70886,9 @@ OUI:70B3D5C50*
 OUI:70B3D5C51*
  ID_OUI_FROM_DATABASE=Innotas Elektronik GmbH
 
+OUI:70B3D5C52*
+ ID_OUI_FROM_DATABASE=sensorway
+
 OUI:70B3D5C53*
  ID_OUI_FROM_DATABASE=S Labs sp. z o.o.
 
@@ -69956,6 +70970,9 @@ OUI:70B3D5C6F*
 OUI:70B3D5C70*
  ID_OUI_FROM_DATABASE=Magnetek
 
+OUI:70B3D5C72*
+ ID_OUI_FROM_DATABASE=Scharco Elektronik GmbH
+
 OUI:70B3D5C73*
  ID_OUI_FROM_DATABASE=C.D.N.CORPORATION
 
@@ -69995,6 +71012,9 @@ OUI:70B3D5C80*
 OUI:70B3D5C81*
  ID_OUI_FROM_DATABASE=DSP DESIGN
 
+OUI:70B3D5C82*
+ ID_OUI_FROM_DATABASE=Sicon srl
+
 OUI:70B3D5C83*
  ID_OUI_FROM_DATABASE=CertusNet Inc.
 
@@ -70043,6 +71063,9 @@ OUI:70B3D5C92*
 OUI:70B3D5C93*
  ID_OUI_FROM_DATABASE=GMI Ltd
 
+OUI:70B3D5C94*
+ ID_OUI_FROM_DATABASE=Vars Technology
+
 OUI:70B3D5C95*
  ID_OUI_FROM_DATABASE=Chengdu Meihuan Technology Co., Ltd
 
@@ -70052,6 +71075,15 @@ OUI:70B3D5C96*
 OUI:70B3D5C97*
  ID_OUI_FROM_DATABASE=CSINFOTEL
 
+OUI:70B3D5C98*
+ ID_OUI_FROM_DATABASE=Trust Automation
+
+OUI:70B3D5C99*
+ ID_OUI_FROM_DATABASE=Remote Diagnostic Technologies Ltd
+
+OUI:70B3D5C9A*
+ ID_OUI_FROM_DATABASE=Todd Digital Limited
+
 OUI:70B3D5C9B*
  ID_OUI_FROM_DATABASE=Tieto Sweden AB
 
@@ -70136,6 +71168,9 @@ OUI:70B3D5CB9*
 OUI:70B3D5CBA*
  ID_OUI_FROM_DATABASE=YUYAMA MFG Co.,Ltd
 
+OUI:70B3D5CBB*
+ ID_OUI_FROM_DATABASE=Postmark Incorporated
+
 OUI:70B3D5CBC*
  ID_OUI_FROM_DATABASE=Procon Electronics Pty Ltd
 
@@ -70391,6 +71426,9 @@ OUI:70B3D5D28*
 OUI:70B3D5D29*
  ID_OUI_FROM_DATABASE=Sportzcast
 
+OUI:70B3D5D2A*
+ ID_OUI_FROM_DATABASE=ITsynergy Ltd
+
 OUI:70B3D5D2B*
  ID_OUI_FROM_DATABASE=StreamPlay Oy Ltd
 
@@ -70589,6 +71627,9 @@ OUI:70B3D5D75*
 OUI:70B3D5D76*
  ID_OUI_FROM_DATABASE=attocube systems AG
 
+OUI:70B3D5D77*
+ ID_OUI_FROM_DATABASE=Portrait Displays, Inc.
+
 OUI:70B3D5D79*
  ID_OUI_FROM_DATABASE=GOMA ELETTRONICA SpA
 
@@ -70682,6 +71723,9 @@ OUI:70B3D5D9D*
 OUI:70B3D5D9E*
  ID_OUI_FROM_DATABASE=Grupo Epelsa S.L.
 
+OUI:70B3D5D9F*
+ ID_OUI_FROM_DATABASE=Digital Solutions JSC
+
 OUI:70B3D5DA1*
  ID_OUI_FROM_DATABASE=Qprel srl
 
@@ -70718,6 +71762,9 @@ OUI:70B3D5DAC*
 OUI:70B3D5DAD*
  ID_OUI_FROM_DATABASE=GD Mission Systems
 
+OUI:70B3D5DAE*
+ ID_OUI_FROM_DATABASE=LGE
+
 OUI:70B3D5DAF*
  ID_OUI_FROM_DATABASE=INNOVATIVE CONCEPTS AND DESIGN LLC
 
@@ -70745,6 +71792,9 @@ OUI:70B3D5DB7*
 OUI:70B3D5DB8*
  ID_OUI_FROM_DATABASE=SISTEM SA
 
+OUI:70B3D5DBB*
+ ID_OUI_FROM_DATABASE=Fuhr GmbH Filtertechnik
+
 OUI:70B3D5DBC*
  ID_OUI_FROM_DATABASE=Gamber Johnson-LLC
 
@@ -70838,6 +71888,9 @@ OUI:70B3D5DDF*
 OUI:70B3D5DE0*
  ID_OUI_FROM_DATABASE=eCozy GmbH
 
+OUI:70B3D5DE1*
+ ID_OUI_FROM_DATABASE=Duplomatic MS spa
+
 OUI:70B3D5DE2*
  ID_OUI_FROM_DATABASE=ACD Elekronik GmbH
 
@@ -70925,6 +71978,9 @@ OUI:70B3D5DFF*
 OUI:70B3D5E00*
  ID_OUI_FROM_DATABASE=Jeaway CCTV Security Ltd,.
 
+OUI:70B3D5E01*
+ ID_OUI_FROM_DATABASE=EarTex
+
 OUI:70B3D5E02*
  ID_OUI_FROM_DATABASE=YEHL & JORDAN LLC
 
@@ -70961,6 +72017,9 @@ OUI:70B3D5E0F*
 OUI:70B3D5E10*
  ID_OUI_FROM_DATABASE=Leidos
 
+OUI:70B3D5E12*
+ ID_OUI_FROM_DATABASE=SNK, Inc.
+
 OUI:70B3D5E14*
  ID_OUI_FROM_DATABASE=Automata Spa
 
@@ -71003,6 +72062,9 @@ OUI:70B3D5E22*
 OUI:70B3D5E23*
  ID_OUI_FROM_DATABASE=Smith Meter, Inc.
 
+OUI:70B3D5E24*
+ ID_OUI_FROM_DATABASE=Gogo Business Aviation
+
 OUI:70B3D5E25*
  ID_OUI_FROM_DATABASE=GJD Manufacturing
 
@@ -71036,12 +72098,18 @@ OUI:70B3D5E2E*
 OUI:70B3D5E30*
  ID_OUI_FROM_DATABASE=QUISS AG
 
+OUI:70B3D5E31*
+ ID_OUI_FROM_DATABASE=NEUROPHET, Inc.
+
 OUI:70B3D5E32*
  ID_OUI_FROM_DATABASE=HERUTU ELECTRONICS CORPORATION
 
 OUI:70B3D5E33*
  ID_OUI_FROM_DATABASE=DEUTA-WERKE GmbH
 
+OUI:70B3D5E34*
+ ID_OUI_FROM_DATABASE=Gamber Johnson-LLC
+
 OUI:70B3D5E35*
  ID_OUI_FROM_DATABASE=Nanospeed Technologies Limited
 
@@ -71099,6 +72167,9 @@ OUI:70B3D5E4B*
 OUI:70B3D5E4C*
  ID_OUI_FROM_DATABASE=IAI-Israel Aerospace Industries MBT
 
+OUI:70B3D5E4D*
+ ID_OUI_FROM_DATABASE=Vulcan Wireless Inc.
+
 OUI:70B3D5E4E*
  ID_OUI_FROM_DATABASE=Midfin Systems
 
@@ -71135,6 +72206,9 @@ OUI:70B3D5E59*
 OUI:70B3D5E5B*
  ID_OUI_FROM_DATABASE=Argosy Labs Inc.
 
+OUI:70B3D5E5C*
+ ID_OUI_FROM_DATABASE=Walton Hi-Tech Industries Ltd.
+
 OUI:70B3D5E5D*
  ID_OUI_FROM_DATABASE=Boffins Technologies AB
 
@@ -71174,6 +72248,9 @@ OUI:70B3D5E70*
 OUI:70B3D5E71*
  ID_OUI_FROM_DATABASE=SiS Technology
 
+OUI:70B3D5E72*
+ ID_OUI_FROM_DATABASE=KDT Corp.
+
 OUI:70B3D5E74*
  ID_OUI_FROM_DATABASE=Exfrontier Co., Ltd.
 
@@ -71309,6 +72386,9 @@ OUI:70B3D5EA3*
 OUI:70B3D5EA4*
  ID_OUI_FROM_DATABASE=Grupo Epelsa S.L.
 
+OUI:70B3D5EA5*
+ ID_OUI_FROM_DATABASE=LOTES TM OOO
+
 OUI:70B3D5EA6*
  ID_OUI_FROM_DATABASE=Galios
 
@@ -71375,6 +72455,9 @@ OUI:70B3D5EBD*
 OUI:70B3D5EBE*
  ID_OUI_FROM_DATABASE=Sierra Pacific Innovations Corp
 
+OUI:70B3D5EBF*
+ ID_OUI_FROM_DATABASE=AUTOMATICA Y REGULACION S.A.
+
 OUI:70B3D5EC1*
  ID_OUI_FROM_DATABASE=Xafax Nederland bv
 
@@ -71459,6 +72542,9 @@ OUI:70B3D5EE4*
 OUI:70B3D5EE5*
  ID_OUI_FROM_DATABASE=Beijing Hzhytech Technology Co.Ltd
 
+OUI:70B3D5EE6*
+ ID_OUI_FROM_DATABASE=Vaunix Technology Corporation
+
 OUI:70B3D5EE7*
  ID_OUI_FROM_DATABASE=BLUE-SOLUTIONS CANADA INC.
 
@@ -71471,6 +72557,9 @@ OUI:70B3D5EE9*
 OUI:70B3D5EEA*
  ID_OUI_FROM_DATABASE=Dameca a/s
 
+OUI:70B3D5EEB*
+ ID_OUI_FROM_DATABASE=shenzhen suofeixiang technology Co.,Ltd
+
 OUI:70B3D5EEC*
  ID_OUI_FROM_DATABASE=Impolux GmbH
 
@@ -71552,6 +72641,9 @@ OUI:70B3D5F07*
 OUI:70B3D5F08*
  ID_OUI_FROM_DATABASE=Szabo Software & Engineering UK Ltd
 
+OUI:70B3D5F09*
+ ID_OUI_FROM_DATABASE=Mictrotrac Retsch GmbH
+
 OUI:70B3D5F0A*
  ID_OUI_FROM_DATABASE=Neuronal Innovation Control S.L.
 
@@ -71627,6 +72719,9 @@ OUI:70B3D5F25*
 OUI:70B3D5F27*
  ID_OUI_FROM_DATABASE=NIRIT- Xinwei  Telecom Technology Co., Ltd.
 
+OUI:70B3D5F28*
+ ID_OUI_FROM_DATABASE=Yi An Electronics Co., Ltd
+
 OUI:70B3D5F29*
  ID_OUI_FROM_DATABASE=SamabaNova Systems
 
@@ -71678,6 +72773,9 @@ OUI:70B3D5F3B*
 OUI:70B3D5F3C*
  ID_OUI_FROM_DATABASE=Gigaray
 
+OUI:70B3D5F3D*
+ ID_OUI_FROM_DATABASE=KAYA Instruments
+
 OUI:70B3D5F3E*
  ID_OUI_FROM_DATABASE=ООО РОНЕКС
 
@@ -71696,6 +72794,9 @@ OUI:70B3D5F44*
 OUI:70B3D5F45*
  ID_OUI_FROM_DATABASE=Norbit ODM AS
 
+OUI:70B3D5F47*
+ ID_OUI_FROM_DATABASE=TXMission Ltd.
+
 OUI:70B3D5F48*
  ID_OUI_FROM_DATABASE=HEITEC AG
 
@@ -71765,6 +72866,9 @@ OUI:70B3D5F62*
 OUI:70B3D5F63*
  ID_OUI_FROM_DATABASE=Ars Products
 
+OUI:70B3D5F64*
+ ID_OUI_FROM_DATABASE=silicom
+
 OUI:70B3D5F65*
  ID_OUI_FROM_DATABASE=MARKUS LABS
 
@@ -71774,6 +72878,12 @@ OUI:70B3D5F67*
 OUI:70B3D5F68*
  ID_OUI_FROM_DATABASE=AL ZAJEL MODERN TELECOMM
 
+OUI:70B3D5F69*
+ ID_OUI_FROM_DATABASE=Copper Labs, Inc.
+
+OUI:70B3D5F6A*
+ ID_OUI_FROM_DATABASE=Guan Show Technologe Co., Ltd.
+
 OUI:70B3D5F6C*
  ID_OUI_FROM_DATABASE=VisioGreen
 
@@ -71912,6 +73022,9 @@ OUI:70B3D5F9C*
 OUI:70B3D5F9E*
  ID_OUI_FROM_DATABASE=International Center for Elementary Particle Physics, The University of Tokyo
 
+OUI:70B3D5F9F*
+ ID_OUI_FROM_DATABASE=M.A.C. Solutions (UK) Ltd
+
 OUI:70B3D5FA0*
  ID_OUI_FROM_DATABASE=TIAMA
 
@@ -71936,6 +73049,9 @@ OUI:70B3D5FA6*
 OUI:70B3D5FA7*
  ID_OUI_FROM_DATABASE=Nordson Corporation
 
+OUI:70B3D5FA8*
+ ID_OUI_FROM_DATABASE=Munters
+
 OUI:70B3D5FA9*
  ID_OUI_FROM_DATABASE=CorDes, LLC
 
@@ -71957,6 +73073,9 @@ OUI:70B3D5FAF*
 OUI:70B3D5FB0*
  ID_OUI_FROM_DATABASE=Rohde&Schwarz Topex SA
 
+OUI:70B3D5FB1*
+ ID_OUI_FROM_DATABASE=TOMEI TSUSHIN KOGYO CO,.LTD
+
 OUI:70B3D5FB2*
  ID_OUI_FROM_DATABASE=KJ3 Elektronik AB
 
@@ -72005,6 +73124,9 @@ OUI:70B3D5FC1*
 OUI:70B3D5FC2*
  ID_OUI_FROM_DATABASE=HUNTER LIBERTY CORPORATION
 
+OUI:70B3D5FC3*
+ ID_OUI_FROM_DATABASE=myUpTech AB
+
 OUI:70B3D5FC5*
  ID_OUI_FROM_DATABASE=Eltwin A/S
 
@@ -72209,6 +73331,9 @@ OUI:70C9C6*
 OUI:70CA4D*
  ID_OUI_FROM_DATABASE=Shenzhen lnovance Technology Co.,Ltd.
 
+OUI:70CA97*
+ ID_OUI_FROM_DATABASE=Ruckus Wireless
+
 OUI:70CA9B*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
@@ -72320,6 +73445,9 @@ OUI:70EF00*
 OUI:70F087*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:70F096*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
 OUI:70F11C*
  ID_OUI_FROM_DATABASE=Shenzhen Ogemray Technology Co.,Ltd
 
@@ -72557,6 +73685,9 @@ OUI:741F4A*
 OUI:741F79*
  ID_OUI_FROM_DATABASE=YOUNGKOOK ELECTRONICS CO.,LTD
 
+OUI:7422BB*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:742344*
  ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
 
@@ -72626,6 +73757,9 @@ OUI:7438B7*
 OUI:743A65*
  ID_OUI_FROM_DATABASE=NEC Corporation
 
+OUI:743AEF*
+ ID_OUI_FROM_DATABASE=Kaonmedia CO., LTD.
+
 OUI:743C18*
  ID_OUI_FROM_DATABASE=Taicang T&W Electronics
 
@@ -72647,6 +73781,9 @@ OUI:74428B*
 OUI:744401*
  ID_OUI_FROM_DATABASE=NETGEAR
 
+OUI:74452D*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:74458A*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
@@ -72686,6 +73823,9 @@ OUI:745612*
 OUI:745798*
  ID_OUI_FROM_DATABASE=TRUMPF Laser GmbH + Co. KG
 
+OUI:7458F3*
+ ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
+
 OUI:745909*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -72900,7 +74040,7 @@ OUI:748E08*
  ID_OUI_FROM_DATABASE=Bestek Corp.
 
 OUI:748EF8*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:748F1B*
  ID_OUI_FROM_DATABASE=MasterImage 3D
@@ -72992,6 +74132,9 @@ OUI:74A78E*
 OUI:74AC5F*
  ID_OUI_FROM_DATABASE=Qiku Internet Network Scientific (Shenzhen) Co., Ltd.
 
+OUI:74ACB9*
+ ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc.
+
 OUI:74ADB7*
  ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd.
 
@@ -73010,6 +74153,9 @@ OUI:74B57E*
 OUI:74B587*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:74B6B6*
+ ID_OUI_FROM_DATABASE=eero inc.
+
 OUI:74B91E*
  ID_OUI_FROM_DATABASE=Nanjing Bestway Automation System Co., Ltd
 
@@ -73052,6 +74198,9 @@ OUI:74C621*
 OUI:74C63B*
  ID_OUI_FROM_DATABASE=AzureWave Technology Inc.
 
+OUI:74C929*
+ ID_OUI_FROM_DATABASE=Zhejiang Dahua Technology Co., Ltd.
+
 OUI:74C99A*
  ID_OUI_FROM_DATABASE=Ericsson AB
 
@@ -73448,6 +74597,9 @@ OUI:7829ED*
 OUI:782A79*
  ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd.
 
+OUI:782B46*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
 OUI:782BCB*
  ID_OUI_FROM_DATABASE=Dell Inc.
 
@@ -73559,6 +74711,9 @@ OUI:784F43*
 OUI:784F9B*
  ID_OUI_FROM_DATABASE=Juniper Networks
 
+OUI:78507C*
+ ID_OUI_FROM_DATABASE=Juniper Networks
+
 OUI:78510C*
  ID_OUI_FROM_DATABASE=LiveU Ltd.
 
@@ -73571,6 +74726,9 @@ OUI:78524A*
 OUI:785262*
  ID_OUI_FROM_DATABASE=Shenzhen Hojy Software Co., Ltd.
 
+OUI:78530D*
+ ID_OUI_FROM_DATABASE=Shenzhen Skyworth  Digital  Technology  CO., Ltd
+
 OUI:785364*
  ID_OUI_FROM_DATABASE=SHIFT GmbH
 
@@ -73586,6 +74744,9 @@ OUI:785517*
 OUI:785712*
  ID_OUI_FROM_DATABASE=Mobile Integration Workgroup
 
+OUI:785773*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:785860*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -73665,7 +74826,7 @@ OUI:787D48*
  ID_OUI_FROM_DATABASE=ITEL MOBILE LIMITED
 
 OUI:787D53*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
 
 OUI:787E61*
  ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -73682,6 +74843,9 @@ OUI:788102*
 OUI:78818F*
  ID_OUI_FROM_DATABASE=Server Racks Australia Pty Ltd
 
+OUI:7881CE*
+ ID_OUI_FROM_DATABASE=China Mobile Iot Limited company
+
 OUI:78843C*
  ID_OUI_FROM_DATABASE=Sony Corporation
 
@@ -73736,6 +74900,9 @@ OUI:78929C*
 OUI:7894B4*
  ID_OUI_FROM_DATABASE=Sercomm Corporation.
 
+OUI:7894E8*
+ ID_OUI_FROM_DATABASE=Radio Bridge
+
 OUI:789682*
  ID_OUI_FROM_DATABASE=zte corporation
 
@@ -73806,7 +74973,7 @@ OUI:78A6BD*
  ID_OUI_FROM_DATABASE=DAEYEON Control&Instrument Co,.Ltd
 
 OUI:78A6E1*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:78A714*
  ID_OUI_FROM_DATABASE=Amphenol
@@ -73817,12 +74984,18 @@ OUI:78A7EB*
 OUI:78A873*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:78AA82*
+ ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd
+
 OUI:78AB60*
  ID_OUI_FROM_DATABASE=ABB Australia
 
 OUI:78ABBB*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:78AC44*
+ ID_OUI_FROM_DATABASE=Dell Inc.
+
 OUI:78ACBF*
  ID_OUI_FROM_DATABASE=Igneous Systems
 
@@ -73868,6 +75041,9 @@ OUI:78B81A*
 OUI:78B84B*
  ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD
 
+OUI:78B8D6*
+ ID_OUI_FROM_DATABASE=Zebra Technologies Inc.
+
 OUI:78BAD0*
  ID_OUI_FROM_DATABASE=Shinybow Technology Co. Ltd.
 
@@ -74745,7 +75921,7 @@ OUI:7C94B2*
  ID_OUI_FROM_DATABASE=Philips Healthcare PCCI
 
 OUI:7C95B1*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
 
 OUI:7C95F3*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -74765,6 +75941,9 @@ OUI:7C9A54*
 OUI:7C9A9B*
  ID_OUI_FROM_DATABASE=VSE valencia smart energy
 
+OUI:7C9EBD*
+ ID_OUI_FROM_DATABASE=Espressif Inc.
+
 OUI:7CA15D*
  ID_OUI_FROM_DATABASE=GN ReSound A/S
 
@@ -74795,6 +75974,9 @@ OUI:7CA97D*
 OUI:7CAB25*
  ID_OUI_FROM_DATABASE=MESMO TECHNOLOGY INC.
 
+OUI:7CAB60*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
 OUI:7CACB2*
  ID_OUI_FROM_DATABASE=Bosch Software Innovations GmbH
 
@@ -75065,6 +76247,9 @@ OUI:7CD844*
 OUI:7CD95C*
  ID_OUI_FROM_DATABASE=Google, Inc.
 
+OUI:7CD9A0*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:7CD9FE*
  ID_OUI_FROM_DATABASE=New Cosmos Electric Co., Ltd.
 
@@ -75086,6 +76271,9 @@ OUI:7CDD76*
 OUI:7CDD90*
  ID_OUI_FROM_DATABASE=Shenzhen Ogemray Technology Co., Ltd.
 
+OUI:7CDFA1*
+ ID_OUI_FROM_DATABASE=Espressif Inc.
+
 OUI:7CE044*
  ID_OUI_FROM_DATABASE=NEON Inc
 
@@ -75131,6 +76319,9 @@ OUI:7CED8D*
 OUI:7CEF18*
  ID_OUI_FROM_DATABASE=Creative Product Design Pty. Ltd.
 
+OUI:7CEF61*
+ ID_OUI_FROM_DATABASE=STR Elektronik Josef Schlechtinger GmbH
+
 OUI:7CEF8A*
  ID_OUI_FROM_DATABASE=Inhon International Ltd.
 
@@ -75302,9 +76493,15 @@ OUI:8020DA*
 OUI:8020E1*
  ID_OUI_FROM_DATABASE=BVBA DPTechnics
 
+OUI:8020FD*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:802275*
  ID_OUI_FROM_DATABASE=Beijing Beny Wave Technology Co Ltd
 
+OUI:8022A7*
+ ID_OUI_FROM_DATABASE=NEC Platforms, Ltd.
+
 OUI:802689*
  ID_OUI_FROM_DATABASE=D-Link International
 
@@ -75818,6 +77015,9 @@ OUI:80CEB9*
 OUI:80CF41*
  ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd.
 
+OUI:80CFA2*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:80D019*
  ID_OUI_FROM_DATABASE=Embed, Inc
 
@@ -76100,6 +77300,9 @@ OUI:842BBC*
 OUI:842C80*
  ID_OUI_FROM_DATABASE=Sichuan Changhong Electric Ltd.
 
+OUI:842E14*
+ ID_OUI_FROM_DATABASE=Silicon Laboratories
+
 OUI:842E27*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
@@ -76175,6 +77378,9 @@ OUI:843DC6*
 OUI:843E79*
  ID_OUI_FROM_DATABASE=Shenzhen Belon Technology CO.,LTD
 
+OUI:843E92*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:843F4E*
  ID_OUI_FROM_DATABASE=Tri-Tech Manufacturing, Inc.
 
@@ -76520,6 +77726,9 @@ OUI:84A1D1*
 OUI:84A24D*
  ID_OUI_FROM_DATABASE=Birds Eye Systems Private Limited
 
+OUI:84A3B5*
+ ID_OUI_FROM_DATABASE=Propulsion systems
+
 OUI:84A423*
  ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
 
@@ -76553,6 +77762,9 @@ OUI:84A9EA*
 OUI:84AA9C*
  ID_OUI_FROM_DATABASE=MitraStar Technology Corp.
 
+OUI:84AB1A*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
 OUI:84ACA4*
  ID_OUI_FROM_DATABASE=Beijing Novel Super Digital TV Technology Co., Ltd
 
@@ -76646,6 +77858,9 @@ OUI:84C9B2*
 OUI:84C9C6*
  ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT
 
+OUI:84CCA8*
+ ID_OUI_FROM_DATABASE=Espressif Inc.
+
 OUI:84CD62*
  ID_OUI_FROM_DATABASE=ShenZhen IDWELL Technology CO.,Ltd
 
@@ -76667,6 +77882,9 @@ OUI:84D47E*
 OUI:84D4C8*
  ID_OUI_FROM_DATABASE=Widex A/S
 
+OUI:84D6C5*
+ ID_OUI_FROM_DATABASE=SolarEdge Technologies
+
 OUI:84D6D0*
  ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
 
@@ -76916,6 +78134,9 @@ OUI:882950*
 OUI:88299C*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:882B94*
+ ID_OUI_FROM_DATABASE=MADOKA SYSTEM Co.,Ltd.
+
 OUI:882BD7*
  ID_OUI_FROM_DATABASE=ADDÉNERGIE  TECHNOLOGIES
 
@@ -76955,6 +78176,9 @@ OUI:88365F*
 OUI:88366C*
  ID_OUI_FROM_DATABASE=EFM Networks
 
+OUI:8836CF*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:883A30*
  ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
 
@@ -76976,6 +78200,9 @@ OUI:883F99*
 OUI:883FD3*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
+OUI:884033*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:88403B*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -77055,7 +78282,7 @@ OUI:885A92*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
 OUI:885BDD*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
 
 OUI:885C47*
  ID_OUI_FROM_DATABASE=Alcatel Lucent
@@ -77289,7 +78516,7 @@ OUI:8891DD*
  ID_OUI_FROM_DATABASE=Racktivity
 
 OUI:889471*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:88947E*
  ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
@@ -77303,6 +78530,9 @@ OUI:8895B9*
 OUI:88964E*
  ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
 
+OUI:889655*
+ ID_OUI_FROM_DATABASE=Zitte corporation
+
 OUI:889676*
  ID_OUI_FROM_DATABASE=TTC MARCONI s.r.o.
 
@@ -77336,6 +78566,9 @@ OUI:889D98*
 OUI:889E33*
  ID_OUI_FROM_DATABASE=TCT mobile ltd
 
+OUI:889E68*
+ ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
+
 OUI:889F6F*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
@@ -77498,6 +78731,9 @@ OUI:88C255*
 OUI:88C36E*
  ID_OUI_FROM_DATABASE=Beijing Ereneben lnformation Technology Limited
 
+OUI:88C397*
+ ID_OUI_FROM_DATABASE=Beijing Xiaomi Mobile Software Co., Ltd
+
 OUI:88C3B3*
  ID_OUI_FROM_DATABASE=SOVICO
 
@@ -77693,6 +78929,9 @@ OUI:8C088B*
 OUI:8C09F4*
  ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
 
+OUI:8C0C87*
+ ID_OUI_FROM_DATABASE=Nokia
+
 OUI:8C0C90*
  ID_OUI_FROM_DATABASE=Ruckus Wireless
 
@@ -77924,6 +79163,9 @@ OUI:8C3A7E*
 OUI:8C3AE3*
  ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
 
+OUI:8C3B32*
+ ID_OUI_FROM_DATABASE=Microfan B.V.
+
 OUI:8C3BAD*
  ID_OUI_FROM_DATABASE=NETGEAR
 
@@ -78056,6 +79298,9 @@ OUI:8C598B*
 OUI:8C59C3*
  ID_OUI_FROM_DATABASE=ADB Italia
 
+OUI:8C59DC*
+ ID_OUI_FROM_DATABASE=ASR Microelectronics (Shanghai) Co., Ltd.
+
 OUI:8C5A25*
  ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
 
@@ -78080,6 +79325,9 @@ OUI:8C5D60*
 OUI:8C5F48*
  ID_OUI_FROM_DATABASE=Continental Intelligent Transportation Systems LLC
 
+OUI:8C5FAD*
+ ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
+
 OUI:8C5FDF*
  ID_OUI_FROM_DATABASE=Beijing Railway Signal Factory
 
@@ -78104,6 +79352,9 @@ OUI:8C640B*
 OUI:8C6422*
  ID_OUI_FROM_DATABASE=Sony Mobile Communications Inc
 
+OUI:8C683A*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:8C6878*
  ID_OUI_FROM_DATABASE=Nortek-AS
 
@@ -78162,7 +79413,7 @@ OUI:8C7CB5*
  ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
 
 OUI:8C7CFF*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:8C7EB3*
  ID_OUI_FROM_DATABASE=Lytro, Inc.
@@ -78179,6 +79430,9 @@ OUI:8C82A8*
 OUI:8C839D*
  ID_OUI_FROM_DATABASE=SHENZHEN XINYUPENG ELECTRONIC TECHNOLOGY CO., LTD
 
+OUI:8C83DF*
+ ID_OUI_FROM_DATABASE=Nokia
+
 OUI:8C83E1*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
@@ -78578,6 +79832,9 @@ OUI:900A39*
 OUI:900A3A*
  ID_OUI_FROM_DATABASE=PSG Plastic Service GmbH
 
+OUI:900A84*
+ ID_OUI_FROM_DATABASE=Mellanox Technologies, Inc.
+
 OUI:900BC1*
  ID_OUI_FROM_DATABASE=Sprocomm Technologies CO.,Ltd
 
@@ -78608,6 +79865,9 @@ OUI:9013DA*
 OUI:901711*
  ID_OUI_FROM_DATABASE=Hagenuk Marinekommunikation GmbH
 
+OUI:90173F*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:90179B*
  ID_OUI_FROM_DATABASE=Nanomegas
 
@@ -78848,6 +80108,9 @@ OUI:905682*
 OUI:905692*
  ID_OUI_FROM_DATABASE=Autotalks Ltd.
 
+OUI:9056FC*
+ ID_OUI_FROM_DATABASE=TECNO MOBILE LIMITED
+
 OUI:905851*
  ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
 
@@ -78860,6 +80123,9 @@ OUI:905C34*
 OUI:905C44*
  ID_OUI_FROM_DATABASE=Compal Broadband Networks, Inc.
 
+OUI:905D7C*
+ ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd
+
 OUI:905F2E*
  ID_OUI_FROM_DATABASE=TCT mobile ltd
 
@@ -78926,6 +80192,9 @@ OUI:907282*
 OUI:90735A*
  ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
 
+OUI:90749D*
+ ID_OUI_FROM_DATABASE=IRay Technology Co., Ltd.
+
 OUI:907841*
  ID_OUI_FROM_DATABASE=Intel Corporate
 
@@ -78995,6 +80264,9 @@ OUI:90895F*
 OUI:908C09*
  ID_OUI_FROM_DATABASE=Total Phase
 
+OUI:908C43*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
 OUI:908C44*
  ID_OUI_FROM_DATABASE=H.K ZONGMU TECHNOLOGY CO., LTD.
 
@@ -79037,6 +80309,9 @@ OUI:9097D5*
 OUI:9097F3*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:909838*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:909864*
  ID_OUI_FROM_DATABASE=Impex-Sat GmbH&amp;Co KG
 
@@ -79046,6 +80321,9 @@ OUI:909916*
 OUI:909A77*
  ID_OUI_FROM_DATABASE=Texas Instruments
 
+OUI:909C4A*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
 OUI:909D7D*
  ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
 
@@ -79094,6 +80372,9 @@ OUI:90AC3F*
 OUI:90ADF7*
  ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
 
+OUI:90ADFC*
+ ID_OUI_FROM_DATABASE=Telechips, Inc.
+
 OUI:90AE1B*
  ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
 
@@ -79122,7 +80403,7 @@ OUI:90B686*
  ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd.
 
 OUI:90B832*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
 
 OUI:90B8D0*
  ID_OUI_FROM_DATABASE=Joyent, Inc.
@@ -79274,9 +80555,51 @@ OUI:90E202*
 OUI:90E2BA*
  ID_OUI_FROM_DATABASE=Intel Corporate
 
+OUI:90E2FC0*
+ ID_OUI_FROM_DATABASE=Pars Ertebat Afzar Co.
+
+OUI:90E2FC1*
+ ID_OUI_FROM_DATABASE=Yite technology
+
+OUI:90E2FC2*
+ ID_OUI_FROM_DATABASE=ShenZhen Temwey Innovation Technology Co.,Ltd.
+
+OUI:90E2FC3*
+ ID_OUI_FROM_DATABASE=Shenzhen Hisource Technology Development CO.,Ltd.
+
+OUI:90E2FC4*
+ ID_OUI_FROM_DATABASE=Dongguan Kangyong electronics technology Co. Ltd
+
+OUI:90E2FC5*
+ ID_OUI_FROM_DATABASE=TOTALONE TECHNOLOGY CO., LTD.
+
+OUI:90E2FC6*
+ ID_OUI_FROM_DATABASE=Sindoh Techno Co., Ltd.
+
+OUI:90E2FC7*
+ ID_OUI_FROM_DATABASE=Fair Winds Digital srl
+
+OUI:90E2FC8*
+ ID_OUI_FROM_DATABASE=bitsensing Inc.
+
+OUI:90E2FC9*
+ ID_OUI_FROM_DATABASE=Huddly AS
+
+OUI:90E2FCA*
+ ID_OUI_FROM_DATABASE=Power Engineering & Manufacturing, Inc.
+
+OUI:90E2FCB*
+ ID_OUI_FROM_DATABASE=Shenzhen Dingsheng Intelligent Technology Co., Ltd
+
+OUI:90E2FCC*
+ ID_OUI_FROM_DATABASE=Stanley Security
+
 OUI:90E2FCD*
  ID_OUI_FROM_DATABASE=Beijing Lanxum Computer Technology CO.,LTD.
 
+OUI:90E2FCE*
+ ID_OUI_FROM_DATABASE=DevCom spol. s r.o.
+
 OUI:90E6BA*
  ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC.
 
@@ -79292,6 +80615,12 @@ OUI:90EA60*
 OUI:90EC50*
  ID_OUI_FROM_DATABASE=C.O.B.O. SPA
 
+OUI:90EC77*
+ ID_OUI_FROM_DATABASE=silicom
+
+OUI:90EEC7*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:90EED9*
  ID_OUI_FROM_DATABASE=UNIVERSAL DE DESARROLLOS ELECTRÓNICOS, SA
 
@@ -79364,6 +80693,51 @@ OUI:94049C*
 OUI:9405B6*
  ID_OUI_FROM_DATABASE=Liling FullRiver Electronics & Technology Ltd
 
+OUI:9405BB0*
+ ID_OUI_FROM_DATABASE=Qingdao Maotran Electronics co., ltd
+
+OUI:9405BB1*
+ ID_OUI_FROM_DATABASE=Dongguan Kingtron Electronics Tech Co., Ltd
+
+OUI:9405BB2*
+ ID_OUI_FROM_DATABASE=Dongguan CXWE Technology Co.,Ltd.
+
+OUI:9405BB3*
+ ID_OUI_FROM_DATABASE=Neurik AG
+
+OUI:9405BB4*
+ ID_OUI_FROM_DATABASE=Shenzhen Baolijie Technology Co., Ltd.
+
+OUI:9405BB5*
+ ID_OUI_FROM_DATABASE=Chengdu Zhongheng Network Co.,Ltd.
+
+OUI:9405BB6*
+ ID_OUI_FROM_DATABASE=ZIGPOS GmbH
+
+OUI:9405BB7*
+ ID_OUI_FROM_DATABASE=LTE-X, Inc
+
+OUI:9405BB8*
+ ID_OUI_FROM_DATABASE=iungo
+
+OUI:9405BB9*
+ ID_OUI_FROM_DATABASE=Zimmer GmbH
+
+OUI:9405BBA*
+ ID_OUI_FROM_DATABASE=SolarEdge Technologies
+
+OUI:9405BBB*
+ ID_OUI_FROM_DATABASE=AUSTAR HEARING SCIENCE AND TECHNILIGY(XIAMEN)CO.,LTD
+
+OUI:9405BBC*
+ ID_OUI_FROM_DATABASE=LAO INDUSTRIA LTDA
+
+OUI:9405BBD*
+ ID_OUI_FROM_DATABASE=Sunthink S&T Development Co.,Ltd
+
+OUI:9405BBE*
+ ID_OUI_FROM_DATABASE=BAE Systems
+
 OUI:940937*
  ID_OUI_FROM_DATABASE=HUMAX Co., Ltd.
 
@@ -79496,6 +80870,9 @@ OUI:9440C9*
 OUI:9441C1*
  ID_OUI_FROM_DATABASE=Mini-Cam Limited
 
+OUI:94434D*
+ ID_OUI_FROM_DATABASE=Ciena Corporation
+
 OUI:944444*
  ID_OUI_FROM_DATABASE=LG Innotek
 
@@ -79646,6 +81023,9 @@ OUI:948854*
 OUI:94885E*
  ID_OUI_FROM_DATABASE=Surfilter Network Technology Co., Ltd.
 
+OUI:948AC6*
+ ID_OUI_FROM_DATABASE=Realme Chongqing Mobile Telecommunications Corp.,Ltd.
+
 OUI:948B03*
  ID_OUI_FROM_DATABASE=EAGET Innovation and Technology Co., Ltd.
 
@@ -79796,6 +81176,9 @@ OUI:94BF2D*
 OUI:94BF80*
  ID_OUI_FROM_DATABASE=zte corporation
 
+OUI:94BF94*
+ ID_OUI_FROM_DATABASE=Juniper Networks
+
 OUI:94BF95*
  ID_OUI_FROM_DATABASE=Shenzhen Coship Electronics Co., Ltd
 
@@ -79838,6 +81221,51 @@ OUI:94C962*
 OUI:94CA0F*
  ID_OUI_FROM_DATABASE=Honeywell Analytics
 
+OUI:94CC040*
+ ID_OUI_FROM_DATABASE=Hangzhou Yongkong Technology Co., Ltd.
+
+OUI:94CC041*
+ ID_OUI_FROM_DATABASE=GOCOAX, INC
+
+OUI:94CC042*
+ ID_OUI_FROM_DATABASE=Nanjing Yacer Communication Technology Co. Ltd.
+
+OUI:94CC043*
+ ID_OUI_FROM_DATABASE=Shenzhen Link technology Co.,Ltd
+
+OUI:94CC044*
+ ID_OUI_FROM_DATABASE=ProConnections, Inc.
+
+OUI:94CC045*
+ ID_OUI_FROM_DATABASE=SHENZHEN SANRAY TECHNOLOGY CO.,LTD
+
+OUI:94CC046*
+ ID_OUI_FROM_DATABASE=Sam Nazarko Trading Ltd
+
+OUI:94CC047*
+ ID_OUI_FROM_DATABASE=Gowing Business And Contracting Wenzhou Co., LTD
+
+OUI:94CC048*
+ ID_OUI_FROM_DATABASE=CircuitWerkes, Inc.
+
+OUI:94CC049*
+ ID_OUI_FROM_DATABASE=ENTEC Electric & Electronic Co., LTD.
+
+OUI:94CC04A*
+ ID_OUI_FROM_DATABASE=hyBee Inc.
+
+OUI:94CC04B*
+ ID_OUI_FROM_DATABASE=Shandong free optical technology co., ltd.
+
+OUI:94CC04C*
+ ID_OUI_FROM_DATABASE=Shanxi Baixin Information Technology Co., Ltd.
+
+OUI:94CC04D*
+ ID_OUI_FROM_DATABASE=Hanzhuo Information Technology(Shanghai) Ltd.
+
+OUI:94CC04E*
+ ID_OUI_FROM_DATABASE=SynchronicIT BV
+
 OUI:94CCB9*
  ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
 
@@ -79898,6 +81326,9 @@ OUI:94D9B3*
 OUI:94DB49*
  ID_OUI_FROM_DATABASE=SITCORP
 
+OUI:94DB56*
+ ID_OUI_FROM_DATABASE=Sony Home Entertainment&Sound Products Inc
+
 OUI:94DBC9*
  ID_OUI_FROM_DATABASE=AzureWave Technology Inc.
 
@@ -79940,12 +81371,18 @@ OUI:94E2FD*
 OUI:94E36D*
  ID_OUI_FROM_DATABASE=Texas Instruments
 
+OUI:94E4BA*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:94E6F7*
  ID_OUI_FROM_DATABASE=Intel Corporate
 
 OUI:94E711*
  ID_OUI_FROM_DATABASE=Xirka Dama Persada PT
 
+OUI:94E7EA*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:94E848*
  ID_OUI_FROM_DATABASE=FYLDE MICRO LTD
 
@@ -79961,6 +81398,9 @@ OUI:94E979*
 OUI:94E98C*
  ID_OUI_FROM_DATABASE=Nokia
 
+OUI:94E9EE*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:94EAEA*
  ID_OUI_FROM_DATABASE=TELLESCOM INDUSTRIA E COMERCIO EM TELECOMUNICACAO
 
@@ -80009,6 +81449,15 @@ OUI:94FAE8*
 OUI:94FB29*
  ID_OUI_FROM_DATABASE=Zebra Technologies Inc.
 
+OUI:94FBA78*
+ ID_OUI_FROM_DATABASE=Silver-I Co.,LTD.
+
+OUI:94FBA79*
+ ID_OUI_FROM_DATABASE=Shanghai Hyco Genyong Technology Co., Ltd.
+
+OUI:94FBA7D*
+ ID_OUI_FROM_DATABASE=Creotech Instruments S.A.
+
 OUI:94FBB2*
  ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT
 
@@ -80027,6 +81476,9 @@ OUI:94FE9D*
 OUI:94FEF4*
  ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
 
+OUI:98006A*
+ ID_OUI_FROM_DATABASE=zte corporation
+
 OUI:980074*
  ID_OUI_FROM_DATABASE=Raisecom Technology CO., LTD
 
@@ -80159,6 +81611,9 @@ OUI:980CA5*
 OUI:980D2E*
  ID_OUI_FROM_DATABASE=HTC Corporation
 
+OUI:980D51*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:980D67*
  ID_OUI_FROM_DATABASE=Zyxel Communications Corporation
 
@@ -80348,6 +81803,9 @@ OUI:98588A*
 OUI:985945*
  ID_OUI_FROM_DATABASE=Texas Instruments
 
+OUI:985949*
+ ID_OUI_FROM_DATABASE=LUXOTTICA GROUP S.P.A.
+
 OUI:985AEB*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -80468,6 +81926,12 @@ OUI:987BF3*
 OUI:987E46*
  ID_OUI_FROM_DATABASE=Emizon Networks Limited
 
+OUI:987ECA*
+ ID_OUI_FROM_DATABASE=Inventus Power Eletronica do Brasil LTDA
+
+OUI:9880EE*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:988217*
  ID_OUI_FROM_DATABASE=Disruptive Ltd
 
@@ -80531,6 +81995,9 @@ OUI:989BCB*
 OUI:989C57*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
+OUI:989D5D*
+ ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
+
 OUI:989E63*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -80639,6 +82106,9 @@ OUI:98C5DB*
 OUI:98C845*
  ID_OUI_FROM_DATABASE=PacketAccess
 
+OUI:98C8B8*
+ ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
+
 OUI:98CA33*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -80690,6 +82160,9 @@ OUI:98DAC4*
 OUI:98DCD9*
  ID_OUI_FROM_DATABASE=UNITEC Co., Ltd.
 
+OUI:98DD5B*
+ ID_OUI_FROM_DATABASE=TAKUMI JAPAN LTD
+
 OUI:98DDEA*
  ID_OUI_FROM_DATABASE=Infinix mobility limited
 
@@ -80730,7 +82203,7 @@ OUI:98EC65*
  ID_OUI_FROM_DATABASE=Cosesy ApS
 
 OUI:98ED5C*
- ID_OUI_FROM_DATABASE=Tesla Motors, Inc
+ ID_OUI_FROM_DATABASE=Tesla,Inc.
 
 OUI:98EECB*
  ID_OUI_FROM_DATABASE=Wistron Infocomm (Zhongshan) Corporation
@@ -80825,6 +82298,9 @@ OUI:98F9C7E*
 OUI:98FA9B*
  ID_OUI_FROM_DATABASE=LCFC(HeFei) Electronics Technology co., ltd
 
+OUI:98FAA7*
+ ID_OUI_FROM_DATABASE=INNONET
+
 OUI:98FAE3*
  ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
 
@@ -80948,6 +82424,9 @@ OUI:9C2DCF*
 OUI:9C2EA1*
  ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
 
+OUI:9C2F4E*
+ ID_OUI_FROM_DATABASE=zte corporation
+
 OUI:9C2F73*
  ID_OUI_FROM_DATABASE=Universal Tiancheng Technology (Beijing) Co., Ltd.
 
@@ -81042,7 +82521,7 @@ OUI:9C431ED*
  ID_OUI_FROM_DATABASE=HK ELEPHONE Communication Tech Co.,Limited
 
 OUI:9C431EE*
- ID_OUI_FROM_DATABASE=Midas Technology DBA Phoenix Audio Technologies
+ ID_OUI_FROM_DATABASE=Midas Technology, Inc. dba Stem Audio / Phoenix Au
 
 OUI:9C443D*
  ID_OUI_FROM_DATABASE=CHENGDU XUGUANG TECHNOLOGY CO, LTD
@@ -81123,7 +82602,7 @@ OUI:9C5CF9*
  ID_OUI_FROM_DATABASE=Sony Mobile Communications Inc
 
 OUI:9C5D12*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
 
 OUI:9C5D95*
  ID_OUI_FROM_DATABASE=VTC Electronics Corp.
@@ -81135,7 +82614,7 @@ OUI:9C5F5A*
  ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
 
 OUI:9C611D*
- ID_OUI_FROM_DATABASE=Omni-ID USA, Inc.
+ ID_OUI_FROM_DATABASE=Panasonic Corporation of North America
 
 OUI:9C6121*
  ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD
@@ -81431,6 +82910,9 @@ OUI:9CB793*
 OUI:9CBB98*
  ID_OUI_FROM_DATABASE=Shen Zhen RND Electronic Co.,LTD
 
+OUI:9CBD6E*
+ ID_OUI_FROM_DATABASE=DERA Co., Ltd
+
 OUI:9CBD9D*
  ID_OUI_FROM_DATABASE=SkyDisk, Inc.
 
@@ -81461,6 +82943,9 @@ OUI:9CC8FC*
 OUI:9CC950*
  ID_OUI_FROM_DATABASE=Baumer Holding
 
+OUI:9CC9EB*
+ ID_OUI_FROM_DATABASE=NETGEAR
+
 OUI:9CCAD9*
  ID_OUI_FROM_DATABASE=Nokia Corporation
 
@@ -81563,9 +83048,15 @@ OUI:9CE951*
 OUI:9CEBE8*
  ID_OUI_FROM_DATABASE=BizLink (Kunshan) Co.,Ltd
 
+OUI:9CEDFA*
+ ID_OUI_FROM_DATABASE=EVUlution AG
+
 OUI:9CEFD5*
  ID_OUI_FROM_DATABASE=Panda Wireless, Inc.
 
+OUI:9CF029*
+ ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd.
+
 OUI:9CF387*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -81845,6 +83336,9 @@ OUI:A0231B*
 OUI:A0239F*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
+OUI:A027B6*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:A028330*
  ID_OUI_FROM_DATABASE=GERSYS GmbH
 
@@ -82016,6 +83510,9 @@ OUI:A0423F*
 OUI:A04246*
  ID_OUI_FROM_DATABASE=IT Telecom Co., Ltd.
 
+OUI:A043B0*
+ ID_OUI_FROM_DATABASE=Hangzhou BroadLink Technology Co.,Ltd
+
 OUI:A043DB*
  ID_OUI_FROM_DATABASE=Sitael S.p.A.
 
@@ -82040,6 +83537,9 @@ OUI:A04E04*
 OUI:A04EA7*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:A04F85*
+ ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
+
 OUI:A04FD4*
  ID_OUI_FROM_DATABASE=ADB Broadband Italia
 
@@ -82514,6 +84014,9 @@ OUI:A0DDE5*
 OUI:A0DE05*
  ID_OUI_FROM_DATABASE=JSC Irbis-T
 
+OUI:A0DE0F*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:A0DF15*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -82610,6 +84113,9 @@ OUI:A0FE61*
 OUI:A0FE91*
  ID_OUI_FROM_DATABASE=AVAT Automation GmbH
 
+OUI:A0FF70*
+ ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
+
 OUI:A400E2*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -82628,6 +84134,9 @@ OUI:A4059E*
 OUI:A407B6*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:A40801*
+ ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
+
 OUI:A408EA*
  ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd.
 
@@ -82724,6 +84233,9 @@ OUI:A41566*
 OUI:A41588*
  ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
 
+OUI:A416E7*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:A41731*
  ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
 
@@ -82772,6 +84284,9 @@ OUI:A42940*
 OUI:A42983*
  ID_OUI_FROM_DATABASE=Boeing Defence Australia
 
+OUI:A42985*
+ ID_OUI_FROM_DATABASE=Sichuan AI-Link Technology Co., Ltd.
+
 OUI:A429B7*
  ID_OUI_FROM_DATABASE=bluesky
 
@@ -82967,6 +84482,9 @@ OUI:A44F29E*
 OUI:A44F29F*
  ID_OUI_FROM_DATABASE=Private
 
+OUI:A45006*
+ ID_OUI_FROM_DATABASE=SHENZHEN HUACHUANG SHIDAI TECHNOLOGYCO.,LTD
+
 OUI:A45046*
  ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
 
@@ -83270,6 +84788,9 @@ OUI:A4B121*
 OUI:A4B197*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:A4B1C1*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
 OUI:A4B1E9*
  ID_OUI_FROM_DATABASE=Technicolor
 
@@ -83345,6 +84866,9 @@ OUI:A4C3F0*
 OUI:A4C494*
  ID_OUI_FROM_DATABASE=Intel Corporate
 
+OUI:A4C54E*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:A4C64F*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -83366,6 +84890,9 @@ OUI:A4CD23*
 OUI:A4CF12*
  ID_OUI_FROM_DATABASE=Espressif Inc.
 
+OUI:A4CFD2*
+ ID_OUI_FROM_DATABASE=Ubee Interactive Co., Limited
+
 OUI:A4D094*
  ID_OUI_FROM_DATABASE=Erwin Peters Systemtechnik GmbH
 
@@ -83436,7 +84963,7 @@ OUI:A4DA229*
  ID_OUI_FROM_DATABASE=Malldon Technology Limited
 
 OUI:A4DA22A*
- ID_OUI_FROM_DATABASE=Abetechs GmbH
+ ID_OUI_FROM_DATABASE=Grundig
 
 OUI:A4DA22B*
  ID_OUI_FROM_DATABASE=Klashwerks Inc.
@@ -83612,6 +85139,9 @@ OUI:A8016D*
 OUI:A80180*
  ID_OUI_FROM_DATABASE=IMAGO Technologies GmbH
 
+OUI:A80577*
+ ID_OUI_FROM_DATABASE=Netlist, Inc.
+
 OUI:A80600*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
@@ -83774,6 +85304,9 @@ OUI:A83FA1E*
 OUI:A84041*
  ID_OUI_FROM_DATABASE=Dragino Technology Co., Limited
 
+OUI:A84122*
+ ID_OUI_FROM_DATABASE=China Mobile (Hangzhou) Information Technology Co.,Ltd.
+
 OUI:A84481*
  ID_OUI_FROM_DATABASE=Nokia Corporation
 
@@ -83870,6 +85403,9 @@ OUI:A8667F*
 OUI:A86A6F*
  ID_OUI_FROM_DATABASE=RIM
 
+OUI:A86ABB*
+ ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+
 OUI:A86AC1*
  ID_OUI_FROM_DATABASE=HanbitEDS Co., Ltd.
 
@@ -84029,6 +85565,9 @@ OUI:A89FEC*
 OUI:A8A089*
  ID_OUI_FROM_DATABASE=Tactical Communications
 
+OUI:A8A097*
+ ID_OUI_FROM_DATABASE=ScioTeq bvba
+
 OUI:A8A159*
  ID_OUI_FROM_DATABASE=ASRock Incorporation
 
@@ -84281,6 +85820,9 @@ OUI:AC0DFE*
 OUI:AC11D3*
  ID_OUI_FROM_DATABASE=Suzhou HOTEK  Video Technology Co. Ltd
 
+OUI:AC1203*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
 OUI:AC1461*
  ID_OUI_FROM_DATABASE=ATAW  Co., Ltd.
 
@@ -84356,6 +85898,9 @@ OUI:AC1DDFE*
 OUI:AC1E92*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,LTD
 
+OUI:AC1ED0*
+ ID_OUI_FROM_DATABASE=Temic Automotive Philippines Inc.
+
 OUI:AC1F6B*
  ID_OUI_FROM_DATABASE=Super Micro Computer, Inc.
 
@@ -84413,6 +85958,9 @@ OUI:AC35EE*
 OUI:AC3613*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:AC3651*
+ ID_OUI_FROM_DATABASE=Jiangsu Hengtong Terahertz Technology Co., Ltd.
+
 OUI:AC3743*
  ID_OUI_FROM_DATABASE=HTC Corporation
 
@@ -84422,6 +85970,9 @@ OUI:AC37C9*
 OUI:AC3870*
  ID_OUI_FROM_DATABASE=Lenovo Mobile Communication Technology Ltd.
 
+OUI:AC3A67*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
 OUI:AC3A7A*
  ID_OUI_FROM_DATABASE=Roku, Inc.
 
@@ -84431,6 +85982,9 @@ OUI:AC3B77*
 OUI:AC3C0B*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:AC3C8E*
+ ID_OUI_FROM_DATABASE=Flextronics Computing(Suzhou)Co.,Ltd.
+
 OUI:AC3CB4*
  ID_OUI_FROM_DATABASE=Nilan A/S
 
@@ -84464,9 +86018,18 @@ OUI:AC4723*
 OUI:AC482D*
  ID_OUI_FROM_DATABASE=Ralinwi Nanjing Electronic Technology Co., Ltd.
 
+OUI:AC4A56*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
+OUI:AC4A67*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
 OUI:AC4AFE*
  ID_OUI_FROM_DATABASE=Hisense Broadband Multimedia Technology Co.,Ltd.
 
+OUI:AC4B1E*
+ ID_OUI_FROM_DATABASE=Integri-Sys.Com LLC
+
 OUI:AC4BC8*
  ID_OUI_FROM_DATABASE=Juniper Networks
 
@@ -84530,6 +86093,9 @@ OUI:AC5E8C*
 OUI:AC5F3E*
  ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND)
 
+OUI:AC6089*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:AC60B6*
  ID_OUI_FROM_DATABASE=Ericsson AB
 
@@ -84611,6 +86177,9 @@ OUI:AC6706*
 OUI:AC676F*
  ID_OUI_FROM_DATABASE=Electrocompaniet A.S.
 
+OUI:AC67B2*
+ ID_OUI_FROM_DATABASE=Espressif Inc.
+
 OUI:AC6B0F*
  ID_OUI_FROM_DATABASE=CADENCE DESIGN SYSTEMS INC
 
@@ -84653,6 +86222,9 @@ OUI:AC7A42*
 OUI:AC7A4D*
  ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
 
+OUI:AC7A56*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
 OUI:AC7BA1*
  ID_OUI_FROM_DATABASE=Intel Corporate
 
@@ -84836,6 +86408,9 @@ OUI:ACBEB6*
 OUI:ACC1EE*
  ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
 
+OUI:ACC25D*
+ ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
+
 OUI:ACC2EC*
  ID_OUI_FROM_DATABASE=CLT INT'L IND. CORP.
 
@@ -84878,6 +86453,9 @@ OUI:ACCABA*
 OUI:ACCB09*
  ID_OUI_FROM_DATABASE=Hefcom Metering (Pty) Ltd
 
+OUI:ACCB51*
+ ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd.
+
 OUI:ACCC8E*
  ID_OUI_FROM_DATABASE=Axis Communications AB
 
@@ -85217,6 +86795,9 @@ OUI:B0416F*
 OUI:B0435D*
  ID_OUI_FROM_DATABASE=NuLEDs, Inc.
 
+OUI:B04502*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:B04515*
  ID_OUI_FROM_DATABASE=mira fitness,LLC.
 
@@ -85430,6 +87011,9 @@ OUI:B09137*
 OUI:B0935B*
  ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
 
+OUI:B09575*
+ ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
+
 OUI:B0958E*
  ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
 
@@ -85502,6 +87086,9 @@ OUI:B0ADAA*
 OUI:B0AE25*
  ID_OUI_FROM_DATABASE=Varikorea
 
+OUI:B0B194*
+ ID_OUI_FROM_DATABASE=zte corporation
+
 OUI:B0B28F*
  ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
 
@@ -85562,6 +87149,9 @@ OUI:B0B3AD*
 OUI:B0B448*
  ID_OUI_FROM_DATABASE=Texas Instruments
 
+OUI:B0B5C3*
+ ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
+
 OUI:B0B5E8*
  ID_OUI_FROM_DATABASE=Ruroc LTD
 
@@ -85685,6 +87275,9 @@ OUI:B0C95B*
 OUI:B0CA68*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:B0CCFE*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:B0CE18*
  ID_OUI_FROM_DATABASE=Zhejiang shenghui lighting co.,Ltd
 
@@ -85739,6 +87332,9 @@ OUI:B0E2E5*
 OUI:B0E39D*
  ID_OUI_FROM_DATABASE=CAT SYSTEM CO.,LTD.
 
+OUI:B0E4D5*
+ ID_OUI_FROM_DATABASE=Google, Inc.
+
 OUI:B0E50E*
  ID_OUI_FROM_DATABASE=NRG SYSTEMS INC
 
@@ -85883,6 +87479,9 @@ OUI:B407F9*
 OUI:B40832*
  ID_OUI_FROM_DATABASE=TC Communications
 
+OUI:B40931*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:B40AC6*
  ID_OUI_FROM_DATABASE=DEXON Systems Ltd.
 
@@ -85916,6 +87515,9 @@ OUI:B41489*
 OUI:B41513*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
+OUI:B4157E*
+ ID_OUI_FROM_DATABASE=Celona Inc.
+
 OUI:B41780*
  ID_OUI_FROM_DATABASE=DTI Group Ltd
 
@@ -85940,6 +87542,9 @@ OUI:B4211D*
 OUI:B4218A*
  ID_OUI_FROM_DATABASE=Dog Hunter LLC
 
+OUI:B42200*
+ ID_OUI_FROM_DATABASE=Brother Industries, LTD.
+
 OUI:B424E7*
  ID_OUI_FROM_DATABASE=Codetek Technology Co.,Ltd
 
@@ -86066,6 +87671,9 @@ OUI:B43DB2*
 OUI:B43E3B*
  ID_OUI_FROM_DATABASE=Viableware, Inc
 
+OUI:B440A4*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
 OUI:B4417A*
  ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT
 
@@ -86210,6 +87818,9 @@ OUI:B46D35*
 OUI:B46D83*
  ID_OUI_FROM_DATABASE=Intel Corporate
 
+OUI:B46E08*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:B47356*
  ID_OUI_FROM_DATABASE=Hangzhou Treebear Networking Co., Ltd.
 
@@ -86249,6 +87860,9 @@ OUI:B47C9C*
 OUI:B47F5E*
  ID_OUI_FROM_DATABASE=Foresight Manufacture (S) Pte Ltd
 
+OUI:B48107*
+ ID_OUI_FROM_DATABASE=SHENZHEN CHUANGWEI-RGB ELECTRONICS CO.,LTD
+
 OUI:B481BF*
  ID_OUI_FROM_DATABASE=Meta-Networks, LLC
 
@@ -86417,6 +88031,9 @@ OUI:B4AE6F*
 OUI:B4B017*
  ID_OUI_FROM_DATABASE=Avaya Inc
 
+OUI:B4B055*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:B4B15A*
  ID_OUI_FROM_DATABASE=Siemens AG Energy Management Division
 
@@ -86498,6 +88115,9 @@ OUI:B4CCE9*
 OUI:B4CD27*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
+OUI:B4CE40*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:B4CEF6*
  ID_OUI_FROM_DATABASE=HTC Corporation
 
@@ -86609,6 +88229,9 @@ OUI:B4EED4*
 OUI:B4EF04*
  ID_OUI_FROM_DATABASE=DAIHAN Scientific Co., Ltd.
 
+OUI:B4EF1C*
+ ID_OUI_FROM_DATABASE=360 AI Technology Co.Ltd
+
 OUI:B4EF39*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
@@ -86618,6 +88241,9 @@ OUI:B4EFFA*
 OUI:B4F0AB*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:B4F18C*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:B4F1DA*
  ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
 
@@ -86657,6 +88283,9 @@ OUI:B4FE8C*
 OUI:B80018*
  ID_OUI_FROM_DATABASE=Htel
 
+OUI:B802A4*
+ ID_OUI_FROM_DATABASE=Aeonsemi, Inc.
+
 OUI:B80305*
  ID_OUI_FROM_DATABASE=Intel Corporate
 
@@ -86762,6 +88391,9 @@ OUI:B82ADC*
 OUI:B82CA0*
  ID_OUI_FROM_DATABASE=Resideo
 
+OUI:B82FCB*
+ ID_OUI_FROM_DATABASE=CMS Electracom
+
 OUI:B830A8*
  ID_OUI_FROM_DATABASE=Road-Track Telematics Development
 
@@ -86873,6 +88505,9 @@ OUI:B8621F*
 OUI:B8634D*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:B86392*
+ ID_OUI_FROM_DATABASE=GUANGDONG GENIUS TECHNOLOGY CO., LTD.
+
 OUI:B863BC*
  ID_OUI_FROM_DATABASE=ROBOTIS, Co, Ltd
 
@@ -86940,7 +88575,7 @@ OUI:B87C6F*
  ID_OUI_FROM_DATABASE=NXP (China) Management Ltd.
 
 OUI:B87CF2*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
 
 OUI:B88198*
  ID_OUI_FROM_DATABASE=Intel Corporate
@@ -86984,6 +88619,9 @@ OUI:B88D12*
 OUI:B88E3A*
  ID_OUI_FROM_DATABASE=Infinite Technologies JLT
 
+OUI:B88E82*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:B88EC6*
  ID_OUI_FROM_DATABASE=Stateless Networks
 
@@ -86996,6 +88634,9 @@ OUI:B88F14*
 OUI:B88FB4*
  ID_OUI_FROM_DATABASE=JABIL CIRCUIT ITALIA S.R.L
 
+OUI:B89047*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
 OUI:B891C9*
  ID_OUI_FROM_DATABASE=Handreamnet
 
@@ -87167,6 +88808,9 @@ OUI:B8C46F*
 OUI:B8C68E*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:B8C6AA*
+ ID_OUI_FROM_DATABASE=Earda Technologies co Ltd
+
 OUI:B8C716*
  ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
 
@@ -87197,12 +88841,18 @@ OUI:B8CD93*
 OUI:B8CDA7*
  ID_OUI_FROM_DATABASE=Maxeler Technologies Ltd.
 
+OUI:B8CEF6*
+ ID_OUI_FROM_DATABASE=Mellanox Technologies, Inc.
+
 OUI:B8D06F*
  ID_OUI_FROM_DATABASE=GUANGZHOU HKUST FOK YING TUNG RESEARCH INSTITUTE
 
 OUI:B8D49D*
  ID_OUI_FROM_DATABASE=M Seven System Ltd.
 
+OUI:B8D4E7*
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
+
 OUI:B8D50B*
  ID_OUI_FROM_DATABASE=Sunitec Enterprise Co.,Ltd
 
@@ -87284,6 +88934,12 @@ OUI:B8DE5E*
 OUI:B8DF6B*
  ID_OUI_FROM_DATABASE=SpotCam Co., Ltd.
 
+OUI:B8E3B1*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
+OUI:B8E3EE*
+ ID_OUI_FROM_DATABASE=Universal Electronics, Inc.
+
 OUI:B8E589*
  ID_OUI_FROM_DATABASE=Payter BV
 
@@ -87317,6 +88973,9 @@ OUI:B8EE79*
 OUI:B8EF8B*
  ID_OUI_FROM_DATABASE=SHENZHEN CANNICE TECHNOLOGY CO.,LTD
 
+OUI:B8F009*
+ ID_OUI_FROM_DATABASE=Espressif Inc.
+
 OUI:B8F080*
  ID_OUI_FROM_DATABASE=SPS, INC.
 
@@ -87386,6 +89045,9 @@ OUI:BC024A*
 OUI:BC0543*
  ID_OUI_FROM_DATABASE=AVM GmbH
 
+OUI:BC0963*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
 OUI:BC0DA5*
  ID_OUI_FROM_DATABASE=Texas Instruments
 
@@ -87395,6 +89057,9 @@ OUI:BC0F2B*
 OUI:BC0F64*
  ID_OUI_FROM_DATABASE=Intel Corporate
 
+OUI:BC0F9A*
+ ID_OUI_FROM_DATABASE=D-Link International
+
 OUI:BC0FA7*
  ID_OUI_FROM_DATABASE=Ouster
 
@@ -87419,6 +89084,9 @@ OUI:BC15AC*
 OUI:BC1665*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
+OUI:BC1695*
+ ID_OUI_FROM_DATABASE=zte corporation
+
 OUI:BC16F5*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
@@ -87479,6 +89147,9 @@ OUI:BC2C55*
 OUI:BC2D98*
  ID_OUI_FROM_DATABASE=ThinGlobal LLC
 
+OUI:BC2DEF*
+ ID_OUI_FROM_DATABASE=Realme Chongqing Mobile Telecommunications Corp.,Ltd.
+
 OUI:BC2E48*
  ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
 
@@ -87503,6 +89174,9 @@ OUI:BC30D9*
 OUI:BC325F*
  ID_OUI_FROM_DATABASE=Zhejiang Dahua Technology Co., Ltd.
 
+OUI:BC33AC*
+ ID_OUI_FROM_DATABASE=Silicon Laboratories
+
 OUI:BC34000*
  ID_OUI_FROM_DATABASE=Redvision CCTV
 
@@ -87593,6 +89267,9 @@ OUI:BC4100*
 OUI:BC4101*
  ID_OUI_FROM_DATABASE=Shenzhen TINNO Mobile Technology Corp.
 
+OUI:BC428C*
+ ID_OUI_FROM_DATABASE=ALPS ELECTRIC CO., LTD.
+
 OUI:BC4377*
  ID_OUI_FROM_DATABASE=Hang Zhou Huite Technology Co.,ltd.
 
@@ -87614,6 +89291,9 @@ OUI:BC4699*
 OUI:BC4760*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:BC4A56*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
 OUI:BC4B79*
  ID_OUI_FROM_DATABASE=SensingTek
 
@@ -87638,6 +89318,9 @@ OUI:BC52B4*
 OUI:BC52B7*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:BC542F*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
 OUI:BC5436*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -87650,6 +89333,9 @@ OUI:BC54F9*
 OUI:BC54FC*
  ID_OUI_FROM_DATABASE=SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD.
 
+OUI:BC5A56*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
 OUI:BC5C4C*
  ID_OUI_FROM_DATABASE=ELECOM CO.,LTD.
 
@@ -87800,6 +89486,9 @@ OUI:BC779F*
 OUI:BC79AD*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:BC7ABF*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:BC7DD1*
  ID_OUI_FROM_DATABASE=Radio Data Comms
 
@@ -88131,7 +89820,7 @@ OUI:BCF2AF*
  ID_OUI_FROM_DATABASE=devolo AG
 
 OUI:BCF310*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
 
 OUI:BCF5AC*
  ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
@@ -88154,6 +89843,9 @@ OUI:BCFE8C*
 OUI:BCFED9*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:BCFF21*
+ ID_OUI_FROM_DATABASE=Smart Code(shenzhen)Technology Co.,Ltd
+
 OUI:BCFFAC*
  ID_OUI_FROM_DATABASE=TOPCON CORPORATION
 
@@ -88196,6 +89888,9 @@ OUI:C0143D*
 OUI:C014B8*
  ID_OUI_FROM_DATABASE=Nokia
 
+OUI:C01692*
+ ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd.
+
 OUI:C0174D*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
@@ -88517,6 +90212,9 @@ OUI:C09132*
 OUI:C09134*
  ID_OUI_FROM_DATABASE=ProCurve Networking by HP
 
+OUI:C095DA*
+ ID_OUI_FROM_DATABASE=NXP India Private Limited
+
 OUI:C09727*
  ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND)
 
@@ -88535,6 +90233,51 @@ OUI:C09A71*
 OUI:C09AD0*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:C09BF40*
+ ID_OUI_FROM_DATABASE=Annapurna labs
+
+OUI:C09BF41*
+ ID_OUI_FROM_DATABASE=Connected Space Management
+
+OUI:C09BF42*
+ ID_OUI_FROM_DATABASE=Hitachi High-Tech Materials Corporation
+
+OUI:C09BF43*
+ ID_OUI_FROM_DATABASE=Osprey Video, Inc
+
+OUI:C09BF44*
+ ID_OUI_FROM_DATABASE=JSC NPK ATRONIK
+
+OUI:C09BF45*
+ ID_OUI_FROM_DATABASE=Infiot Inc.
+
+OUI:C09BF46*
+ ID_OUI_FROM_DATABASE=LTD Delovoy Office
+
+OUI:C09BF47*
+ ID_OUI_FROM_DATABASE=Big Dutchman International GmbH
+
+OUI:C09BF48*
+ ID_OUI_FROM_DATABASE=SHENZHEN WINS ELECTRONIC TECHNOLOGY CO., LTD
+
+OUI:C09BF49*
+ ID_OUI_FROM_DATABASE=Alcatraz AI Inc.
+
+OUI:C09BF4A*
+ ID_OUI_FROM_DATABASE=Inveo
+
+OUI:C09BF4B*
+ ID_OUI_FROM_DATABASE=NUCTECH COMPANY LIMITED
+
+OUI:C09BF4C*
+ ID_OUI_FROM_DATABASE=Pinpark Inc.
+
+OUI:C09BF4D*
+ ID_OUI_FROM_DATABASE=The Professional Monitor Company Ltd
+
+OUI:C09BF4E*
+ ID_OUI_FROM_DATABASE=Continental Automotive Component Malaysia Sdn.Bhd.
+
 OUI:C09C04*
  ID_OUI_FROM_DATABASE=Shaanxi GuoLian Digital TV Technology Co.,Ltd.
 
@@ -88589,6 +90332,9 @@ OUI:C0A5DD*
 OUI:C0A600*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:C0A66D*
+ ID_OUI_FROM_DATABASE=Inspur Group Co., Ltd.
+
 OUI:C0A8F0*
  ID_OUI_FROM_DATABASE=Adamson Systems Engineering
 
@@ -88598,6 +90344,9 @@ OUI:C0AA68*
 OUI:C0AC54*
  ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
 
+OUI:C0B101*
+ ID_OUI_FROM_DATABASE=zte corporation
+
 OUI:C0B339*
  ID_OUI_FROM_DATABASE=Comigo Ltd.
 
@@ -88899,7 +90648,7 @@ OUI:C412F5*
  ID_OUI_FROM_DATABASE=D-Link International
 
 OUI:C413E2*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
 
 OUI:C4143C*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -88970,6 +90719,9 @@ OUI:C42F90*
 OUI:C43018*
  ID_OUI_FROM_DATABASE=MCS Logic Inc.
 
+OUI:C432D1*
+ ID_OUI_FROM_DATABASE=Farlink Technology Limited
+
 OUI:C43306*
  ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd.
 
@@ -89015,6 +90767,9 @@ OUI:C4411E*
 OUI:C44202*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:C44268*
+ ID_OUI_FROM_DATABASE=CRESTRON ELECTRONICS, INC.
+
 OUI:C4438F*
  ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications)
 
@@ -89276,6 +91031,9 @@ OUI:C486E9*
 OUI:C488E5*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:C489ED*
+ ID_OUI_FROM_DATABASE=Solid Optics EU N.V.
+
 OUI:C48A5A*
  ID_OUI_FROM_DATABASE=JFCONTROL
 
@@ -89387,6 +91145,9 @@ OUI:C49FF3*
 OUI:C4A366*
  ID_OUI_FROM_DATABASE=zte corporation
 
+OUI:C4A402*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:C4A81D*
  ID_OUI_FROM_DATABASE=D-Link International
 
@@ -89574,7 +91335,7 @@ OUI:C4F464*
  ID_OUI_FROM_DATABASE=Spica international
 
 OUI:C4F57C*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:C4F5A5*
  ID_OUI_FROM_DATABASE=Kumalift Co., Ltd.
@@ -89666,6 +91427,9 @@ OUI:C803F5*
 OUI:C80718*
  ID_OUI_FROM_DATABASE=TDSi
 
+OUI:C80739*
+ ID_OUI_FROM_DATABASE=NAKAYO Inc
+
 OUI:C80873*
  ID_OUI_FROM_DATABASE=Ruckus Wireless
 
@@ -89918,6 +91682,9 @@ OUI:C85645*
 OUI:C85663*
  ID_OUI_FROM_DATABASE=Sunflex Europe GmbH
 
+OUI:C858C0*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
 OUI:C85A9F*
  ID_OUI_FROM_DATABASE=zte corporation
 
@@ -89988,10 +91755,10 @@ OUI:C8662C*
  ID_OUI_FROM_DATABASE=Beijing Haitai Fangyuan High Technology Co,.Ltd.
 
 OUI:C8665D*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
 
 OUI:C8675E*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
 
 OUI:C869CD*
  ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -90008,6 +91775,9 @@ OUI:C86CB6*
 OUI:C86F1D*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:C87125*
+ ID_OUI_FROM_DATABASE=Johnson Outdoors Marine Electronics d/b/a Minnkota
+
 OUI:C87248*
  ID_OUI_FROM_DATABASE=Aplicom Oy
 
@@ -90035,6 +91805,9 @@ OUI:C87D77*
 OUI:C87E75*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:C88314*
+ ID_OUI_FROM_DATABASE=Tempo Communications
+
 OUI:C88439*
  ID_OUI_FROM_DATABASE=Sunrise Technologies
 
@@ -90059,6 +91832,9 @@ OUI:C88A83*
 OUI:C88B47*
  ID_OUI_FROM_DATABASE=Nolangroup S.P.A con Socio Unico
 
+OUI:C88BE8*
+ ID_OUI_FROM_DATABASE=Masimo Corporation
+
 OUI:C88D83*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -90230,6 +92006,9 @@ OUI:C8BBD3*
 OUI:C8BCC8*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:C8BCE5*
+ ID_OUI_FROM_DATABASE=Sense Things Japan INC.
+
 OUI:C8BE19*
  ID_OUI_FROM_DATABASE=D-Link International
 
@@ -90311,6 +92090,9 @@ OUI:C8D69D*
 OUI:C8D719*
  ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC
 
+OUI:C8D778*
+ ID_OUI_FROM_DATABASE=BSH Hausgeraete GmbH
+
 OUI:C8D779*
  ID_OUI_FROM_DATABASE=QING DAO HAIER TELECOM CO.,LTD.
 
@@ -90494,6 +92276,9 @@ OUI:CC0CDA*
 OUI:CC0DEC*
  ID_OUI_FROM_DATABASE=Cisco SPVTG
 
+OUI:CC0DF2*
+ ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
+
 OUI:CC10A3*
  ID_OUI_FROM_DATABASE=Beijing Nan Bao Technology Co., Ltd.
 
@@ -90713,6 +92498,9 @@ OUI:CC3FEA*
 OUI:CC40D0*
  ID_OUI_FROM_DATABASE=NETGEAR
 
+OUI:CC418E*
+ ID_OUI_FROM_DATABASE=MSA Innovation
+
 OUI:CC43E3*
  ID_OUI_FROM_DATABASE=Trump s.a.
 
@@ -90744,7 +92532,7 @@ OUI:CC4D38*
  ID_OUI_FROM_DATABASE=Carnegie Technologies
 
 OUI:CC4E24*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:CC4EEC*
  ID_OUI_FROM_DATABASE=HUMAX Co., Ltd.
@@ -90890,6 +92678,12 @@ OUI:CC7D37*
 OUI:CC7EE7*
  ID_OUI_FROM_DATABASE=Panasonic Corporation AVC Networks Company
 
+OUI:CC7F75*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
+OUI:CC7F76*
+ ID_OUI_FROM_DATABASE=Cisco Systems, Inc
+
 OUI:CC81DA*
  ID_OUI_FROM_DATABASE=Phicomm (Shanghai) Co., Ltd.
 
@@ -90992,6 +92786,12 @@ OUI:CCA4AF*
 OUI:CCA614*
  ID_OUI_FROM_DATABASE=AIFA TECHNOLOGY CORP.
 
+OUI:CCA7C1*
+ ID_OUI_FROM_DATABASE=Google, Inc.
+
+OUI:CCAB2C*
+ ID_OUI_FROM_DATABASE=HUMAX Co., Ltd.
+
 OUI:CCAF78*
  ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
 
@@ -91193,6 +92993,9 @@ OUI:CCD3C1*
 OUI:CCD3E2*
  ID_OUI_FROM_DATABASE=Jiangsu Yinhe  Electronics Co.,Ltd.
 
+OUI:CCD42E*
+ ID_OUI_FROM_DATABASE=Arcadyan Corporation
+
 OUI:CCD4A1*
  ID_OUI_FROM_DATABASE=MitraStar Technology Corp.
 
@@ -91283,6 +93086,9 @@ OUI:CCF954*
 OUI:CCF957*
  ID_OUI_FROM_DATABASE=u-blox AG
 
+OUI:CCF9E4*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
 OUI:CCF9E8*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
@@ -91310,6 +93116,9 @@ OUI:D0034B*
 OUI:D003DF*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:D003EB*
+ ID_OUI_FROM_DATABASE=Texas Instruments
+
 OUI:D00401*
  ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
 
@@ -91487,6 +93296,9 @@ OUI:D03DC3*
 OUI:D03E5C*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
+OUI:D03FAA*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
 OUI:D041C9*
  ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
 
@@ -91649,6 +93461,9 @@ OUI:D0634D*
 OUI:D063B4*
  ID_OUI_FROM_DATABASE=SolidRun Ltd.
 
+OUI:D06544*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
 OUI:D065CA*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -91883,6 +93698,9 @@ OUI:D0B2C4*
 OUI:D0B33F*
  ID_OUI_FROM_DATABASE=Shenzhen TINNO Mobile Technology Corp.
 
+OUI:D0B45D*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:D0B498*
  ID_OUI_FROM_DATABASE=Robert Bosch LLC Automotive Electronics
 
@@ -92018,6 +93836,9 @@ OUI:D0D286*
 OUI:D0D2B0*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
+OUI:D0D3E0*
+ ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
+
 OUI:D0D3FC*
  ID_OUI_FROM_DATABASE=Mios, Ltd.
 
@@ -92210,6 +94031,9 @@ OUI:D4136F*
 OUI:D41A3F*
  ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
 
+OUI:D41AC8*
+ ID_OUI_FROM_DATABASE=Nippon Printer Engineering
+
 OUI:D41C1C*
  ID_OUI_FROM_DATABASE=RCF S.P.A.
 
@@ -92237,6 +94061,9 @@ OUI:D4223F*
 OUI:D4224E*
  ID_OUI_FROM_DATABASE=Alcatel Lucent
 
+OUI:D422CD*
+ ID_OUI_FROM_DATABASE=Xsens Technologies B.V.
+
 OUI:D42493*
  ID_OUI_FROM_DATABASE=GW Technologies Co.,Ltd
 
@@ -92417,6 +94244,9 @@ OUI:D45251*
 OUI:D45297*
  ID_OUI_FROM_DATABASE=nSTREAMS Technologies, Inc.
 
+OUI:D452EE*
+ ID_OUI_FROM_DATABASE=BSkyB Ltd
+
 OUI:D45383*
  ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd.
 
@@ -92447,6 +94277,9 @@ OUI:D45D64*
 OUI:D45DDF*
  ID_OUI_FROM_DATABASE=PEGATRON CORPORATION
 
+OUI:D45EEC*
+ ID_OUI_FROM_DATABASE=Beijing Xiaomi Electronics Co., Ltd.
+
 OUI:D45F25*
  ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd
 
@@ -92651,6 +94484,9 @@ OUI:D4883F*
 OUI:D48890*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:D48A39*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:D48CB5*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
@@ -92801,6 +94637,9 @@ OUI:D4B92F*
 OUI:D4BBC8*
  ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
 
+OUI:D4BBE6*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:D4BD1E*
  ID_OUI_FROM_DATABASE=5VT Technologies,Taiwan LTd.
 
@@ -92856,7 +94695,7 @@ OUI:D4CF37*
  ID_OUI_FROM_DATABASE=Symbolic IO
 
 OUI:D4CFF9*
- ID_OUI_FROM_DATABASE=Shenzhen Sen5 Technology Co., Ltd.
+ ID_OUI_FROM_DATABASE=Shenzhen SEI Robotics Co.,Ltd
 
 OUI:D4D184*
  ID_OUI_FROM_DATABASE=ADB Broadband Italia
@@ -92867,6 +94706,9 @@ OUI:D4D249*
 OUI:D4D252*
  ID_OUI_FROM_DATABASE=Intel Corporate
 
+OUI:D4D2D6*
+ ID_OUI_FROM_DATABASE=FN-LINK TECHNOLOGY LIMITED
+
 OUI:D4D2E5*
  ID_OUI_FROM_DATABASE=BKAV Corporation
 
@@ -92885,6 +94727,12 @@ OUI:D4D898*
 OUI:D4D919*
  ID_OUI_FROM_DATABASE=GoPro
 
+OUI:D4DACD*
+ ID_OUI_FROM_DATABASE=BSkyB Ltd
+
+OUI:D4DC09*
+ ID_OUI_FROM_DATABASE=Mist Systems, Inc.
+
 OUI:D4DCCD*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -92978,6 +94826,9 @@ OUI:D8052E*
 OUI:D806D1*
  ID_OUI_FROM_DATABASE=Honeywell Fire System (Shanghai) Co,. Ltd.
 
+OUI:D807B6*
+ ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
+
 OUI:D80831*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
@@ -93054,7 +94905,7 @@ OUI:D81EDE*
  ID_OUI_FROM_DATABASE=B&W Group Ltd
 
 OUI:D81FCC*
- ID_OUI_FROM_DATABASE=Brocade Communications Systems, Inc.
+ ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC
 
 OUI:D8209F*
  ID_OUI_FROM_DATABASE=Cubro Acronet GesmbH
@@ -93167,6 +95018,9 @@ OUI:D84606*
 OUI:D84710*
  ID_OUI_FROM_DATABASE=Sichuan Changhong Electric Ltd.
 
+OUI:D84732*
+ ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
+
 OUI:D848EE*
  ID_OUI_FROM_DATABASE=Hangzhou Xueji Technology Co., Ltd.
 
@@ -93182,6 +95036,12 @@ OUI:D84A87*
 OUI:D84B2A*
  ID_OUI_FROM_DATABASE=Cognitas Technologies, Inc.
 
+OUI:D84C90*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
+OUI:D84DB9*
+ ID_OUI_FROM_DATABASE=Wu Qi Technologies,Inc.
+
 OUI:D84FB8*
  ID_OUI_FROM_DATABASE=LG ELECTRONICS
 
@@ -93192,7 +95052,7 @@ OUI:D8543A*
  ID_OUI_FROM_DATABASE=Texas Instruments
 
 OUI:D854A2*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
 
 OUI:D85575*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
@@ -93224,6 +95084,9 @@ OUI:D85DEF*
 OUI:D85DFB*
  ID_OUI_FROM_DATABASE=Private
 
+OUI:D85F77*
+ ID_OUI_FROM_DATABASE=Telink Semiconductor (Shanghai) Co., Ltd.
+
 OUI:D860B0*
  ID_OUI_FROM_DATABASE=bioMérieux Italia S.p.A.
 
@@ -93284,6 +95147,9 @@ OUI:D87533*
 OUI:D8760A*
  ID_OUI_FROM_DATABASE=Escort, Inc.
 
+OUI:D8787F*
+ ID_OUI_FROM_DATABASE=Ubee Interactive Co., Limited
+
 OUI:D878E5*
  ID_OUI_FROM_DATABASE=KUHN SA
 
@@ -93389,6 +95255,9 @@ OUI:D890E8*
 OUI:D8912A*
  ID_OUI_FROM_DATABASE=Zyxel Communications Corporation
 
+OUI:D89136*
+ ID_OUI_FROM_DATABASE=Dover Fueling Solutions
+
 OUI:D89341*
  ID_OUI_FROM_DATABASE=General Electric Global Research
 
@@ -93467,6 +95336,9 @@ OUI:D8A6FD*
 OUI:D8A756*
  ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
 
+OUI:D8A8C8*
+ ID_OUI_FROM_DATABASE=zte corporation
+
 OUI:D8A98B*
  ID_OUI_FROM_DATABASE=Texas Instruments
 
@@ -93551,6 +95423,9 @@ OUI:D8C497*
 OUI:D8C4E9*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:D8C561*
+ ID_OUI_FROM_DATABASE=CommFront Communications Pte Ltd
+
 OUI:D8C691*
  ID_OUI_FROM_DATABASE=Hichan Technology Corp.
 
@@ -93851,11 +95726,14 @@ OUI:DC31D1*
 OUI:DC330D*
  ID_OUI_FROM_DATABASE=QING DAO HAIER TELECOM CO.,LTD.
 
+OUI:DC333D*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:DC3350*
  ID_OUI_FROM_DATABASE=TechSAT GmbH
 
 OUI:DC35F1*
- ID_OUI_FROM_DATABASE=Positivo Informática SA.
+ ID_OUI_FROM_DATABASE=Positivo Tecnologia S.A.
 
 OUI:DC3714*
  ID_OUI_FROM_DATABASE=Apple, Inc.
@@ -93906,7 +95784,7 @@ OUI:DC44270*
  ID_OUI_FROM_DATABASE=Suritel
 
 OUI:DC44271*
- ID_OUI_FROM_DATABASE=Tesla Motors, Inc
+ ID_OUI_FROM_DATABASE=Tesla,Inc.
 
 OUI:DC44272*
  ID_OUI_FROM_DATABASE=Skywave Technology Co,.Ltd.
@@ -94043,6 +95921,9 @@ OUI:DC68EB*
 OUI:DC6AEA*
  ID_OUI_FROM_DATABASE=Infinix mobility limited
 
+OUI:DC6B12*
+ ID_OUI_FROM_DATABASE=worldcns inc.
+
 OUI:DC6DCD*
  ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
 
@@ -94106,6 +95987,9 @@ OUI:DC9088*
 OUI:DC962C*
  ID_OUI_FROM_DATABASE=NST Audio Ltd
 
+OUI:DC9840*
+ ID_OUI_FROM_DATABASE=Microsoft Corporation
+
 OUI:DC9914*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -94506,7 +96390,7 @@ OUI:E01AEA*
  ID_OUI_FROM_DATABASE=Allied Telesis, Inc.
 
 OUI:E01C41*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
 
 OUI:E01CEE*
  ID_OUI_FROM_DATABASE=Bravo Tech, Inc.
@@ -94529,6 +96413,9 @@ OUI:E01F88*
 OUI:E02202*
  ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
 
+OUI:E023FF*
+ ID_OUI_FROM_DATABASE=Fortinet, Inc.
+
 OUI:E0247F*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -95096,6 +96983,12 @@ OUI:E0D1E6*
 OUI:E0D31A*
  ID_OUI_FROM_DATABASE=EQUES Technology Co., Limited
 
+OUI:E0D462*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
+OUI:E0D4E8*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
 OUI:E0D55E*
  ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD.
 
@@ -95132,6 +97025,9 @@ OUI:E0DCFF*
 OUI:E0DDC0*
  ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
 
+OUI:E0E0FC*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:E0E5CF*
  ID_OUI_FROM_DATABASE=Texas Instruments
 
@@ -95186,6 +97082,9 @@ OUI:E0F5C6*
 OUI:E0F5CA*
  ID_OUI_FROM_DATABASE=CHENG UEI PRECISION INDUSTRY CO.,LTD.
 
+OUI:E0F6B5*
+ ID_OUI_FROM_DATABASE=Nintendo Co.,Ltd
+
 OUI:E0F847*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -95306,6 +97205,9 @@ OUI:E425E7*
 OUI:E425E9*
  ID_OUI_FROM_DATABASE=Color-Chip
 
+OUI:E42686*
+ ID_OUI_FROM_DATABASE=DWnet Technologies(Suzhou) Corporation
+
 OUI:E42771*
  ID_OUI_FROM_DATABASE=Smartlabs
 
@@ -95360,6 +97262,9 @@ OUI:E4388C*
 OUI:E438F2*
  ID_OUI_FROM_DATABASE=Advantage Controls
 
+OUI:E43A65*
+ ID_OUI_FROM_DATABASE=MofiNetwork Inc
+
 OUI:E43A6E*
  ID_OUI_FROM_DATABASE=Shenzhen Zeroone Technology CO.,LTD
 
@@ -95501,6 +97406,9 @@ OUI:E45D52*
 OUI:E45D75*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:E45E37*
+ ID_OUI_FROM_DATABASE=Intel Corporate
+
 OUI:E46059*
  ID_OUI_FROM_DATABASE=Pingtek Co., Ltd.
 
@@ -95588,6 +97496,9 @@ OUI:E481B3*
 OUI:E482CC*
  ID_OUI_FROM_DATABASE=Jumptronic GmbH
 
+OUI:E48326*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:E48399*
  ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
 
@@ -95618,6 +97529,9 @@ OUI:E49069*
 OUI:E4907E*
  ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company
 
+OUI:E490FD*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
 OUI:E4922A*
  ID_OUI_FROM_DATABASE=DBG HOLDINGS LIMITED
 
@@ -95861,6 +97775,9 @@ OUI:E4E0A6*
 OUI:E4E0C5*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:E4E112*
+ ID_OUI_FROM_DATABASE=Texas Instruments
+
 OUI:E4E130*
  ID_OUI_FROM_DATABASE=TCT mobile ltd
 
@@ -95897,6 +97814,9 @@ OUI:E4F327*
 OUI:E4F365*
  ID_OUI_FROM_DATABASE=Time-O-Matic, Inc.
 
+OUI:E4F3C4*
+ ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
+
 OUI:E4F3E3*
  ID_OUI_FROM_DATABASE=Shanghai iComhome Co.,Ltd.
 
@@ -96368,6 +98288,9 @@ OUI:E89606*
 OUI:E8986D*
  ID_OUI_FROM_DATABASE=Palo Alto Networks
 
+OUI:E898C2*
+ ID_OUI_FROM_DATABASE=ZETLAB Company
+
 OUI:E8995A*
  ID_OUI_FROM_DATABASE=PiiGAB, Processinformation i Goteborg AB
 
@@ -96425,6 +98348,51 @@ OUI:E8B2AC*
 OUI:E8B2FE*
  ID_OUI_FROM_DATABASE=HUMAX Co., Ltd.
 
+OUI:E8B4700*
+ ID_OUI_FROM_DATABASE=DongGuan Ramaxel Memory Technology
+
+OUI:E8B4701*
+ ID_OUI_FROM_DATABASE=Autocom Diagnostic Partner AB
+
+OUI:E8B4702*
+ ID_OUI_FROM_DATABASE=internet domain name system beijing engineering research center ltd
+
+OUI:E8B4703*
+ ID_OUI_FROM_DATABASE=Webfleet Solutions B.V.
+
+OUI:E8B4704*
+ ID_OUI_FROM_DATABASE=YAWATA ELECTRIC INDUSTRIAL CO.,LTD.
+
+OUI:E8B4705*
+ ID_OUI_FROM_DATABASE=Alperia Fiber srl
+
+OUI:E8B4706*
+ ID_OUI_FROM_DATABASE=Elcoma
+
+OUI:E8B4707*
+ ID_OUI_FROM_DATABASE=Tibit Communications
+
+OUI:E8B4708*
+ ID_OUI_FROM_DATABASE=DEHN SE + Co KG
+
+OUI:E8B4709*
+ ID_OUI_FROM_DATABASE=Miltek Industries Pte Ltd
+
+OUI:E8B470A*
+ ID_OUI_FROM_DATABASE=plc2 Design GmbH
+
+OUI:E8B470B*
+ ID_OUI_FROM_DATABASE=Digifocus Technology Inc.
+
+OUI:E8B470C*
+ ID_OUI_FROM_DATABASE=Anduril Industries
+
+OUI:E8B470D*
+ ID_OUI_FROM_DATABASE=Medica Corporation
+
+OUI:E8B470E*
+ ID_OUI_FROM_DATABASE=UNICACCES GROUPE
+
 OUI:E8B4AE*
  ID_OUI_FROM_DATABASE=Shenzhen C&D Electronics Co.,Ltd
 
@@ -96578,6 +98546,9 @@ OUI:E8E875*
 OUI:E8E8B7*
  ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd.
 
+OUI:E8E98E*
+ ID_OUI_FROM_DATABASE=SOLAR controls s.r.o.
+
 OUI:E8EA6A*
  ID_OUI_FROM_DATABASE=StarTech.com
 
@@ -96746,6 +98717,9 @@ OUI:EC2E4E*
 OUI:EC3091*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
+OUI:EC316D*
+ ID_OUI_FROM_DATABASE=Hansgrohe
+
 OUI:EC3586*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -96869,6 +98843,9 @@ OUI:EC6264*
 OUI:EC63E5*
  ID_OUI_FROM_DATABASE=ePBoard Design LLC
 
+OUI:EC63ED*
+ ID_OUI_FROM_DATABASE=Hyundai Autoever Corp.
+
 OUI:EC64E7*
  ID_OUI_FROM_DATABASE=MOCACARE Corporation
 
@@ -96884,6 +98861,9 @@ OUI:EC6881*
 OUI:EC6C9F*
  ID_OUI_FROM_DATABASE=Chengdu Volans Technology CO.,LTD
 
+OUI:EC6CB5*
+ ID_OUI_FROM_DATABASE=zte corporation
+
 OUI:EC6F0B*
  ID_OUI_FROM_DATABASE=FADU, Inc.
 
@@ -96896,6 +98876,9 @@ OUI:EC71DB*
 OUI:EC74BA*
  ID_OUI_FROM_DATABASE=Hirschmann Automation and Control GmbH
 
+OUI:EC7949*
+ ID_OUI_FROM_DATABASE=FUJITSU LIMITED
+
 OUI:EC79F2*
  ID_OUI_FROM_DATABASE=Startel
 
@@ -96983,6 +98966,9 @@ OUI:EC93ED*
 OUI:EC9681*
  ID_OUI_FROM_DATABASE=2276427 Ontario Inc
 
+OUI:EC97B2*
+ ID_OUI_FROM_DATABASE=SUMEC Machinery & Electric Co.,Ltd.
+
 OUI:EC986C*
  ID_OUI_FROM_DATABASE=Lufft Mess- und Regeltechnik GmbH
 
@@ -97280,6 +99266,9 @@ OUI:F0038C*
 OUI:F00786*
  ID_OUI_FROM_DATABASE=Shandong Bittel Electronics Co., Ltd
 
+OUI:F008D1*
+ ID_OUI_FROM_DATABASE=Espressif Inc.
+
 OUI:F008F1*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
@@ -97298,6 +99287,9 @@ OUI:F00EBF*
 OUI:F00FEC*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
+OUI:F01090*
+ ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd
+
 OUI:F010AB*
  ID_OUI_FROM_DATABASE=China Mobile (Hangzhou) Information Technology Co., Ltd.
 
@@ -97436,6 +99428,9 @@ OUI:F02FD8*
 OUI:F0321A*
  ID_OUI_FROM_DATABASE=Mita-Teknik A/S
 
+OUI:F033E5*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:F03404*
  ID_OUI_FROM_DATABASE=TCT mobile ltd
 
@@ -97469,6 +99464,9 @@ OUI:F03FF8*
 OUI:F0407B*
  ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
 
+OUI:F041C6*
+ ID_OUI_FROM_DATABASE=Heat Tech Company, Ltd.
+
 OUI:F041C80*
  ID_OUI_FROM_DATABASE=LINPA ACOUSTIC TECHNOLOGY CO.,LTD
 
@@ -97670,6 +99668,9 @@ OUI:F079E8*
 OUI:F07BCB*
  ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd.
 
+OUI:F07CC7*
+ ID_OUI_FROM_DATABASE=Juniper Networks
+
 OUI:F07D68*
  ID_OUI_FROM_DATABASE=D-Link Corporation
 
@@ -97682,6 +99683,9 @@ OUI:F07F0C*
 OUI:F08173*
  ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
 
+OUI:F08175*
+ ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
+
 OUI:F081AF*
  ID_OUI_FROM_DATABASE=IRZ AUTOMATION TECHNOLOGIES LTD
 
@@ -97758,7 +99762,7 @@ OUI:F09CD7*
  ID_OUI_FROM_DATABASE=Guangzhou Blue Cheetah Intelligent Technology Co., Ltd.
 
 OUI:F09CE9*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
 
 OUI:F09E63*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
@@ -97850,6 +99854,9 @@ OUI:F0AF85*
 OUI:F0B014*
  ID_OUI_FROM_DATABASE=AVM Audiovisuelles Marketing und Computersysteme GmbH
 
+OUI:F0B022*
+ ID_OUI_FROM_DATABASE=TOHO Electronics INC.
+
 OUI:F0B052*
  ID_OUI_FROM_DATABASE=Ruckus Wireless
 
@@ -98066,6 +100073,9 @@ OUI:F0F644*
 OUI:F0F669*
  ID_OUI_FROM_DATABASE=Motion Analysis Corporation
 
+OUI:F0F6C1*
+ ID_OUI_FROM_DATABASE=Sonos, Inc.
+
 OUI:F0F755*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
@@ -98099,6 +100109,9 @@ OUI:F40304*
 OUI:F40321*
  ID_OUI_FROM_DATABASE=BeNeXt B.V.
 
+OUI:F4032A*
+ ID_OUI_FROM_DATABASE=Amazon Technologies Inc.
+
 OUI:F4032F*
  ID_OUI_FROM_DATABASE=Reduxio Systems
 
@@ -98207,6 +100220,9 @@ OUI:F415FD*
 OUI:F417B8*
  ID_OUI_FROM_DATABASE=AirTies Wireless Networks
 
+OUI:F419E2*
+ ID_OUI_FROM_DATABASE=Volterra
+
 OUI:F41BA1*
  ID_OUI_FROM_DATABASE=Apple, Inc.
 
@@ -98255,6 +100271,9 @@ OUI:F42C56*
 OUI:F42E7F*
  ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company
 
+OUI:F4308B*
+ ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
+
 OUI:F430B9*
  ID_OUI_FROM_DATABASE=Hewlett Packard
 
@@ -98348,6 +100367,9 @@ OUI:F450EB*
 OUI:F45214*
  ID_OUI_FROM_DATABASE=Mellanox Technologies, Inc.
 
+OUI:F45420*
+ ID_OUI_FROM_DATABASE=TELLESCOM INDUSTRIA E COMERCIO EM TELECOMUNICACAO
+
 OUI:F45433*
  ID_OUI_FROM_DATABASE=Rockwell Automation
 
@@ -98435,6 +100457,9 @@ OUI:F470AB*
 OUI:F47190*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:F47335*
+ ID_OUI_FROM_DATABASE=Logitech Far East
+
 OUI:F473CA*
  ID_OUI_FROM_DATABASE=Conversion Sound Inc.
 
@@ -98480,6 +100505,9 @@ OUI:F485C6*
 OUI:F48771*
  ID_OUI_FROM_DATABASE=Infoblox
 
+OUI:F487C5*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:F48B32*
  ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd
 
@@ -98501,12 +100529,60 @@ OUI:F48E92*
 OUI:F490CA*
  ID_OUI_FROM_DATABASE=Tensorcom
 
+OUI:F490CB0*
+ ID_OUI_FROM_DATABASE=Epitel, Inc.
+
+OUI:F490CB1*
+ ID_OUI_FROM_DATABASE=DELEM BV
+
+OUI:F490CB2*
+ ID_OUI_FROM_DATABASE=ICE Gateway GmbH
+
+OUI:F490CB3*
+ ID_OUI_FROM_DATABASE=Ricker Lyman Robotic
+
+OUI:F490CB4*
+ ID_OUI_FROM_DATABASE=OmniNet
+
+OUI:F490CB5*
+ ID_OUI_FROM_DATABASE=Avilution
+
+OUI:F490CB6*
+ ID_OUI_FROM_DATABASE=Airbeam Wireless Technologies Inc.
+
+OUI:F490CB7*
+ ID_OUI_FROM_DATABASE=TEQ SA
+
+OUI:F490CB8*
+ ID_OUI_FROM_DATABASE=Beijing Penslink Co., Ltd.
+
+OUI:F490CB9*
+ ID_OUI_FROM_DATABASE=Fractyl Labs
+
+OUI:F490CBA*
+ ID_OUI_FROM_DATABASE=Private
+
+OUI:F490CBB*
+ ID_OUI_FROM_DATABASE=A-dec Inc.
+
+OUI:F490CBC*
+ ID_OUI_FROM_DATABASE=Cheetah Medical
+
+OUI:F490CBD*
+ ID_OUI_FROM_DATABASE=Simavita (Aust) Pty Ltd
+
+OUI:F490CBE*
+ ID_OUI_FROM_DATABASE=RSAE Labs Inc
+
 OUI:F490EA*
  ID_OUI_FROM_DATABASE=Deciso B.V.
 
 OUI:F4911E*
  ID_OUI_FROM_DATABASE=ZHUHAI EWPE INFORMATION TECHNOLOGY INC
 
+OUI:F492BF*
+ ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc.
+
 OUI:F4939F*
  ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co., Ltd.
 
@@ -98546,6 +100622,9 @@ OUI:F49FF3*
 OUI:F4A294*
  ID_OUI_FROM_DATABASE=EAGLE WORLD DEVELOPMENT CO., LIMITED
 
+OUI:F4A4D6*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:F4A52A*
  ID_OUI_FROM_DATABASE=Hawa Technologies Inc
 
@@ -98594,6 +100673,9 @@ OUI:F4B6E5*
 OUI:F4B72A*
  ID_OUI_FROM_DATABASE=TIME INTERCONNECT LTD
 
+OUI:F4B78D*
+ ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
+
 OUI:F4B7B3*
  ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd.
 
@@ -98693,6 +100775,9 @@ OUI:F4D9C6*
 OUI:F4D9FB*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:F4DBE3*
+ ID_OUI_FROM_DATABASE=Apple, Inc.
+
 OUI:F4DBE6*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
@@ -98751,11 +100836,14 @@ OUI:F4EA67*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
 OUI:F4EAB5*
- ID_OUI_FROM_DATABASE=Aerohive Networks Inc.
+ ID_OUI_FROM_DATABASE=Extreme Networks, Inc.
 
 OUI:F4EB38*
  ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS
 
+OUI:F4EB9F*
+ ID_OUI_FROM_DATABASE=Ellu Company 2019 SL
+
 OUI:F4EC38*
  ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD.
 
@@ -99149,6 +101237,9 @@ OUI:F84E73*
 OUI:F84F57*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
+OUI:F84FAD*
+ ID_OUI_FROM_DATABASE=Hui Zhou Gaoshengda Technology Co.,LTD
+
 OUI:F8501C*
  ID_OUI_FROM_DATABASE=Tianjin Geneuo Technology Co.,Ltd
 
@@ -99470,6 +101561,9 @@ OUI:F8ADCB*
 OUI:F8AE27*
  ID_OUI_FROM_DATABASE=John Deere Electronic Solutions
 
+OUI:F8AF05*
+ ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd.
+
 OUI:F8AFDB*
  ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD
 
@@ -99920,12 +102014,18 @@ OUI:FC35E6*
 OUI:FC372B*
  ID_OUI_FROM_DATABASE=SICHUAN TIANYI COMHEART TELECOMCO.,LTD
 
+OUI:FC3964*
+ ID_OUI_FROM_DATABASE=ITEL MOBILE LIMITED
+
 OUI:FC3CE9*
  ID_OUI_FROM_DATABASE=Tsingtong Technologies Co, Ltd.
 
 OUI:FC3D93*
  ID_OUI_FROM_DATABASE=LONGCHEER TELECOMMUNICATION LIMITED
 
+OUI:FC3DA5*
+ ID_OUI_FROM_DATABASE=Arcadyan Corporation
+
 OUI:FC3F7C*
  ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD
 
@@ -100049,6 +102149,9 @@ OUI:FC6DC0*
 OUI:FC6FB7*
  ID_OUI_FROM_DATABASE=ARRIS Group, Inc.
 
+OUI:FC71FA*
+ ID_OUI_FROM_DATABASE=Trane Technologies
+
 OUI:FC7516*
  ID_OUI_FROM_DATABASE=D-Link International
 
@@ -100130,6 +102233,9 @@ OUI:FC94CE*
 OUI:FC94E3*
  ID_OUI_FROM_DATABASE=Technicolor CH USA Inc.
 
+OUI:FC956A*
+ ID_OUI_FROM_DATABASE=OCTAGON SYSTEMS CORP.
+
 OUI:FC9947*
  ID_OUI_FROM_DATABASE=Cisco Systems, Inc
 
@@ -100394,6 +102500,9 @@ OUI:FCDD55*
 OUI:FCDE90*
  ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd
 
+OUI:FCE14F*
+ ID_OUI_FROM_DATABASE=BRK Brands, Inc.
+
 OUI:FCE186*
  ID_OUI_FROM_DATABASE=A3M Co., LTD
 
index 2b55c61a71163168c1ea10a084154d3c78c8bd07..efd26dc1ef521a5ce7ab63e14782cb4c6f2b9dab 100644 (file)
@@ -216,6 +216,9 @@ acpi:OVTI*:
 acpi:PEGA*:
  ID_VENDOR_FROM_DATABASE=Pegatron Corporation
 
+acpi:PHYT*:
+ ID_VENDOR_FROM_DATABASE=Phytium Technology Co. Ltd.
+
 acpi:QCOM*:
  ID_VENDOR_FROM_DATABASE=Qualcomm Inc
 
index 8deb7e57023d51141cae41759c06b8a189762be2..fff78628715593c255f6b23ebc7a44c8f83fc5ec 100644 (file)
@@ -1,5 +1,5 @@
---- 20-acpi-vendor.hwdb.base   2019-11-29 14:29:51.816965218 +0100
-+++ 20-acpi-vendor.hwdb        2019-11-29 14:29:51.821964280 +0100
+--- 20-acpi-vendor.hwdb.base   2020-03-06 12:40:11.417307950 +0100
++++ 20-acpi-vendor.hwdb        2020-03-06 12:40:11.433308177 +0100
 @@ -3,6 +3,8 @@
  # Data imported from:
  #     https://uefi.org/uefi-pnp-export
@@ -19,7 +19,7 @@
  acpi:AMDI*:
   ID_VENDOR_FROM_DATABASE=AMD
  
-@@ -283,6 +282,9 @@
+@@ -286,6 +285,9 @@
  acpi:AAA*:
   ID_VENDOR_FROM_DATABASE=Avolites Ltd
  
@@ -29,7 +29,7 @@
  acpi:AAE*:
   ID_VENDOR_FROM_DATABASE=Anatek Electronics Inc.
  
-@@ -310,6 +312,9 @@
+@@ -313,6 +315,9 @@
  acpi:ABO*:
   ID_VENDOR_FROM_DATABASE=D-Link Systems Inc
  
@@ -39,7 +39,7 @@
  acpi:ABS*:
   ID_VENDOR_FROM_DATABASE=Abaco Systems, Inc.
  
-@@ -355,7 +360,7 @@
+@@ -358,7 +363,7 @@
  acpi:ACO*:
   ID_VENDOR_FROM_DATABASE=Allion Computer Inc.
  
@@ -48,7 +48,7 @@
   ID_VENDOR_FROM_DATABASE=Aspen Tech Inc
  
  acpi:ACR*:
-@@ -628,6 +633,9 @@
+@@ -631,6 +636,9 @@
  acpi:AMT*:
   ID_VENDOR_FROM_DATABASE=AMT International Industry
  
@@ -58,7 +58,7 @@
  acpi:AMX*:
   ID_VENDOR_FROM_DATABASE=AMX LLC
  
-@@ -676,6 +684,9 @@
+@@ -679,6 +687,9 @@
  acpi:AOA*:
   ID_VENDOR_FROM_DATABASE=AOpen Inc.
  
@@ -68,7 +68,7 @@
  acpi:AOE*:
   ID_VENDOR_FROM_DATABASE=Advanced Optics Electronics, Inc.
  
-@@ -685,6 +696,9 @@
+@@ -688,6 +699,9 @@
  acpi:AOT*:
   ID_VENDOR_FROM_DATABASE=Alcatel
  
@@ -78,7 +78,7 @@
  acpi:APC*:
   ID_VENDOR_FROM_DATABASE=American Power Conversion
  
-@@ -860,7 +874,7 @@
+@@ -863,7 +877,7 @@
   ID_VENDOR_FROM_DATABASE=Alps Electric Inc
  
  acpi:AUO*:
@@ -87,7 +87,7 @@
  
  acpi:AUR*:
   ID_VENDOR_FROM_DATABASE=Aureal Semiconductor
-@@ -940,6 +954,9 @@
+@@ -943,6 +957,9 @@
  acpi:AXE*:
   ID_VENDOR_FROM_DATABASE=Axell Corporation
  
@@ -97,7 +97,7 @@
  acpi:AXI*:
   ID_VENDOR_FROM_DATABASE=American Magnetics
  
-@@ -1090,6 +1107,9 @@
+@@ -1093,6 +1110,9 @@
  acpi:BML*:
   ID_VENDOR_FROM_DATABASE=BIOMED Lab
  
  acpi:BMS*:
   ID_VENDOR_FROM_DATABASE=BIOMEDISYS
  
-@@ -1102,6 +1122,9 @@
+@@ -1105,6 +1125,9 @@
  acpi:BNO*:
   ID_VENDOR_FROM_DATABASE=Bang & Olufsen
  
  acpi:BNS*:
   ID_VENDOR_FROM_DATABASE=Boulder Nonlinear Systems
  
-@@ -1342,6 +1365,9 @@
+@@ -1345,6 +1368,9 @@
  acpi:CHA*:
   ID_VENDOR_FROM_DATABASE=Chase Research PLC
  
  acpi:CHD*:
   ID_VENDOR_FROM_DATABASE=ChangHong Electric Co.,Ltd
  
-@@ -1495,6 +1521,9 @@
+@@ -1498,6 +1524,9 @@
  acpi:COD*:
   ID_VENDOR_FROM_DATABASE=CODAN Pty. Ltd.
  
  acpi:COI*:
   ID_VENDOR_FROM_DATABASE=Codec Inc.
  
-@@ -1901,7 +1930,7 @@
+@@ -1904,7 +1933,7 @@
   ID_VENDOR_FROM_DATABASE=Dragon Information Technology
  
  acpi:DJE*:
  
  acpi:DJP*:
   ID_VENDOR_FROM_DATABASE=Maygay Machines, Ltd
-@@ -2233,6 +2262,9 @@
+@@ -2236,6 +2265,9 @@
  acpi:EIN*:
   ID_VENDOR_FROM_DATABASE=Elegant Invention
  
  acpi:EKA*:
   ID_VENDOR_FROM_DATABASE=MagTek Inc.
  
-@@ -2494,6 +2526,9 @@
+@@ -2497,6 +2529,9 @@
  acpi:FCG*:
   ID_VENDOR_FROM_DATABASE=First International Computer Ltd
  
  acpi:FCS*:
   ID_VENDOR_FROM_DATABASE=Focus Enhancements, Inc.
  
-@@ -2867,7 +2902,7 @@
+@@ -2870,7 +2905,7 @@
   ID_VENDOR_FROM_DATABASE=General Standards Corporation
  
  acpi:GSM*:
  
  acpi:GSN*:
   ID_VENDOR_FROM_DATABASE=Grandstream Networks, Inc.
-@@ -2968,6 +3003,9 @@
+@@ -2971,6 +3006,9 @@
  acpi:HEC*:
   ID_VENDOR_FROM_DATABASE=Hisense Electric Co., Ltd.
  
  acpi:HEL*:
   ID_VENDOR_FROM_DATABASE=Hitachi Micro Systems Europe Ltd
  
-@@ -3097,6 +3135,9 @@
+@@ -3100,6 +3138,9 @@
  acpi:HSD*:
   ID_VENDOR_FROM_DATABASE=HannStar Display Corp
  
  acpi:HSM*:
   ID_VENDOR_FROM_DATABASE=AT&T Microelectronics
  
-@@ -3220,6 +3261,9 @@
+@@ -3223,6 +3264,9 @@
  acpi:ICI*:
   ID_VENDOR_FROM_DATABASE=Infotek Communication Inc
  
  acpi:ICM*:
   ID_VENDOR_FROM_DATABASE=Intracom SA
  
-@@ -3316,6 +3360,9 @@
+@@ -3319,6 +3363,9 @@
  acpi:IKE*:
   ID_VENDOR_FROM_DATABASE=Ikegami Tsushinki Co. Ltd.
  
  acpi:IKS*:
   ID_VENDOR_FROM_DATABASE=Ikos Systems Inc
  
-@@ -3361,6 +3408,9 @@
+@@ -3364,6 +3411,9 @@
  acpi:IMT*:
   ID_VENDOR_FROM_DATABASE=Inmax Technology Corporation
  
  acpi:INA*:
   ID_VENDOR_FROM_DATABASE=Inventec Corporation
  
-@@ -3868,6 +3918,9 @@
+@@ -3871,6 +3921,9 @@
  acpi:LAN*:
   ID_VENDOR_FROM_DATABASE=Sodeman Lancom Inc
  
  acpi:LAS*:
   ID_VENDOR_FROM_DATABASE=LASAT Comm. A/S
  
-@@ -3913,6 +3966,9 @@
+@@ -3916,6 +3969,9 @@
  acpi:LED*:
   ID_VENDOR_FROM_DATABASE=Long Engineering Design Inc
  
  acpi:LEG*:
   ID_VENDOR_FROM_DATABASE=Legerity, Inc
  
-@@ -3928,6 +3984,9 @@
+@@ -3931,6 +3987,9 @@
  acpi:LGC*:
   ID_VENDOR_FROM_DATABASE=Logic Ltd
  
  acpi:LGI*:
   ID_VENDOR_FROM_DATABASE=Logitech Inc
  
-@@ -3982,6 +4041,9 @@
+@@ -3985,6 +4044,9 @@
  acpi:LND*:
   ID_VENDOR_FROM_DATABASE=Land Computer Company Ltd
  
  acpi:LNK*:
   ID_VENDOR_FROM_DATABASE=Link Tech Inc
  
-@@ -4016,7 +4078,7 @@
+@@ -4019,7 +4081,7 @@
   ID_VENDOR_FROM_DATABASE=Design Technology
  
  acpi:LPL*:
  
  acpi:LSC*:
   ID_VENDOR_FROM_DATABASE=LifeSize Communications
-@@ -4192,6 +4254,9 @@
+@@ -4195,6 +4257,9 @@
  acpi:MCX*:
   ID_VENDOR_FROM_DATABASE=Millson Custom Solutions Inc.
  
  acpi:MDA*:
   ID_VENDOR_FROM_DATABASE=Media4 Inc
  
-@@ -4429,6 +4494,9 @@
+@@ -4432,6 +4497,9 @@
  acpi:MOM*:
   ID_VENDOR_FROM_DATABASE=Momentum Data Systems
  
  acpi:MOS*:
   ID_VENDOR_FROM_DATABASE=Moses Corporation
  
-@@ -4654,6 +4722,9 @@
+@@ -4657,6 +4725,9 @@
  acpi:NAL*:
   ID_VENDOR_FROM_DATABASE=Network Alchemy
  
  acpi:NAT*:
   ID_VENDOR_FROM_DATABASE=NaturalPoint Inc.
  
-@@ -5158,6 +5229,9 @@
+@@ -5161,6 +5232,9 @@
  acpi:PCX*:
   ID_VENDOR_FROM_DATABASE=PC Xperten
  
  acpi:PDM*:
   ID_VENDOR_FROM_DATABASE=Psion Dacom Plc.
  
-@@ -5221,9 +5295,6 @@
+@@ -5224,9 +5298,6 @@
  acpi:PHE*:
   ID_VENDOR_FROM_DATABASE=Philips Medical Systems Boeblingen GmbH
  
  acpi:PHL*:
   ID_VENDOR_FROM_DATABASE=Philips Consumer Electronics Company
  
-@@ -5311,9 +5382,6 @@
+@@ -5314,9 +5385,6 @@
  acpi:PNL*:
   ID_VENDOR_FROM_DATABASE=Panelview, Inc.
  
  acpi:PNR*:
   ID_VENDOR_FROM_DATABASE=Planar Systems, Inc.
  
-@@ -5449,15 +5517,9 @@
+@@ -5452,15 +5520,9 @@
  acpi:PTS*:
   ID_VENDOR_FROM_DATABASE=Plain Tree Systems Inc
  
  acpi:PVG*:
   ID_VENDOR_FROM_DATABASE=Proview Global Co., Ltd
  
-@@ -5773,9 +5835,6 @@
+@@ -5776,9 +5838,6 @@
  acpi:RTI*:
   ID_VENDOR_FROM_DATABASE=Rancho Tech Inc
  
  acpi:RTL*:
   ID_VENDOR_FROM_DATABASE=Realtek Semiconductor Company Ltd
  
-@@ -5941,9 +6000,6 @@
+@@ -5944,9 +6003,6 @@
  acpi:SEE*:
   ID_VENDOR_FROM_DATABASE=SeeColor Corporation
  
  acpi:SEI*:
   ID_VENDOR_FROM_DATABASE=Seitz & Associates Inc
  
-@@ -6400,6 +6456,9 @@
+@@ -6403,6 +6459,9 @@
  acpi:SVD*:
   ID_VENDOR_FROM_DATABASE=SVD Computer
  
  acpi:SVI*:
   ID_VENDOR_FROM_DATABASE=Sun Microsystems
  
-@@ -6484,6 +6543,9 @@
+@@ -6487,6 +6546,9 @@
  acpi:SZM*:
   ID_VENDOR_FROM_DATABASE=Shenzhen MTC Co., Ltd
  
  acpi:TAA*:
   ID_VENDOR_FROM_DATABASE=Tandberg
  
-@@ -6574,6 +6636,9 @@
+@@ -6577,6 +6639,9 @@
  acpi:TDG*:
   ID_VENDOR_FROM_DATABASE=Six15 Technologies
  
  acpi:TDM*:
   ID_VENDOR_FROM_DATABASE=Tandem Computer Europe Inc
  
-@@ -6616,6 +6681,9 @@
+@@ -6619,6 +6684,9 @@
  acpi:TEV*:
   ID_VENDOR_FROM_DATABASE=Televés, S.A.
  
  acpi:TEZ*:
   ID_VENDOR_FROM_DATABASE=Tech Source Inc.
  
-@@ -6730,9 +6798,6 @@
+@@ -6733,9 +6801,6 @@
  acpi:TNC*:
   ID_VENDOR_FROM_DATABASE=TNC Industrial Company Ltd
  
  acpi:TNM*:
   ID_VENDOR_FROM_DATABASE=TECNIMAGEN SA
  
-@@ -7039,14 +7104,14 @@
+@@ -7042,14 +7107,14 @@
  acpi:UNC*:
   ID_VENDOR_FROM_DATABASE=Unisys Corporation
  
  
  acpi:UNI*:
   ID_VENDOR_FROM_DATABASE=Uniform Industry Corp.
-@@ -7081,6 +7146,9 @@
+@@ -7084,6 +7149,9 @@
  acpi:USA*:
   ID_VENDOR_FROM_DATABASE=Utimaco Safeware AG
  
  acpi:USD*:
   ID_VENDOR_FROM_DATABASE=U.S. Digital Corporation
  
-@@ -7324,9 +7392,6 @@
+@@ -7327,9 +7395,6 @@
  acpi:WAL*:
   ID_VENDOR_FROM_DATABASE=Wave Access
  
  acpi:WAV*:
   ID_VENDOR_FROM_DATABASE=Wavephore
  
-@@ -7451,7 +7516,7 @@
+@@ -7454,7 +7519,7 @@
   ID_VENDOR_FROM_DATABASE=WyreStorm Technologies LLC
  
  acpi:WYS*:
  
  acpi:WYT*:
   ID_VENDOR_FROM_DATABASE=Wooyoung Image & Information Co.,Ltd.
-@@ -7465,9 +7530,6 @@
+@@ -7468,9 +7533,6 @@
  acpi:XDM*:
   ID_VENDOR_FROM_DATABASE=XDM Ltd.
  
  acpi:XES*:
   ID_VENDOR_FROM_DATABASE=Extreme Engineering Solutions, Inc.
  
-@@ -7498,9 +7560,6 @@
+@@ -7501,9 +7563,6 @@
  acpi:XNT*:
   ID_VENDOR_FROM_DATABASE=XN Technologies, Inc.
  
  acpi:XQU*:
   ID_VENDOR_FROM_DATABASE=SHANGHAI SVA-DAV ELECTRONICS CO., LTD
  
-@@ -7567,6 +7626,9 @@
+@@ -7570,6 +7629,9 @@
  acpi:ZBX*:
   ID_VENDOR_FROM_DATABASE=Zebax Technologies
  
index 5c328d03826d985febd073ddd20e8047501d0174..f26bd3b81b8f50f38275593ff5567852c4965814 100644 (file)
@@ -116,6 +116,9 @@ pci:v0000018A*
 pci:v0000018Ad00000106*
  ID_MODEL_FROM_DATABASE=FPC-0106TX misprogrammed [RTL81xx]
 
+pci:v000001DE*
+ ID_VENDOR_FROM_DATABASE=Oxide Computer Company
+
 pci:v0000021B*
  ID_VENDOR_FROM_DATABASE=Compaq Computer Corporation
 
@@ -1214,6 +1217,12 @@ pci:v00001000d0000005Dsv000017AAsd00001052*
 pci:v00001000d0000005Dsv000017AAsd00001053*
  ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (ThinkServer RAID 720ix)
 
+pci:v00001000d0000005Dsv00001BD4sd00000014*
+ ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (12G SAS3108 2G)
+
+pci:v00001000d0000005Dsv00001BD4sd00000015*
+ ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (12G SAS3108 4G)
+
 pci:v00001000d0000005Dsv00001D49sd00000600*
  ID_MODEL_FROM_DATABASE=MegaRAID SAS-3 3108 [Invader] (ThinkSystem RAID 730-8i 1GB Cache PCIe 12Gb Adapter)
 
@@ -1427,6 +1436,21 @@ pci:v00001000d00000072sv00001028sd00001F20*
 pci:v00001000d00000072sv00001028sd00001F22*
  ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (PERC H200 Internal Tape Adapter)
 
+pci:v00001000d00000072sv00001734sd00001177*
+ ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (HBA Ctrl SAS 6G 0/1 [D2607])
+
+pci:v00001000d00000072sv00001BD4sd0000000D*
+ ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (6G SAS2008IT)
+
+pci:v00001000d00000072sv00001BD4sd0000000E*
+ ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (6G SAS2008IR)
+
+pci:v00001000d00000072sv00001BD4sd0000000F*
+ ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (6G SAS2008IT SA5248)
+
+pci:v00001000d00000072sv00001BD4sd00000010*
+ ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (6G SAS2008IR SA5248)
+
 pci:v00001000d00000072sv00008086sd0000350F*
  ID_MODEL_FROM_DATABASE=SAS2008 PCI-Express Fusion-MPT SAS-2 [Falcon] (RMS2LL040 RAID Controller)
 
@@ -1751,6 +1775,12 @@ pci:v00001000d00000087sv00001590sd00000042*
 pci:v00001000d00000087sv00001590sd00000044*
  ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2 (H220i)
 
+pci:v00001000d00000087sv00001BD4sd00000009*
+ ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2 (6G SAS2308IR)
+
+pci:v00001000d00000087sv00001BD4sd0000000A*
+ ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2 (6G SAS2308IT)
+
 pci:v00001000d00000087sv00008086sd00003000*
  ID_MODEL_FROM_DATABASE=SAS2308 PCI-Express Fusion-MPT SAS-2 (RS25GB008 RAID Controller)
 
@@ -1832,9 +1862,27 @@ pci:v00001000d00000097sv00001028sd00001FD3*
 pci:v00001000d00000097sv000015D9sd00000808*
  ID_MODEL_FROM_DATABASE=SAS3008 PCI-Express Fusion-MPT SAS-3 (AOC-S3008L-L8e)
 
+pci:v00001000d00000097sv00001BD4sd0000000B*
+ ID_MODEL_FROM_DATABASE=SAS3008 PCI-Express Fusion-MPT SAS-3 (12G SAS3008IR)
+
+pci:v00001000d00000097sv00001BD4sd0000000C*
+ ID_MODEL_FROM_DATABASE=SAS3008 PCI-Express Fusion-MPT SAS-3 (12G SAS3008IT)
+
 pci:v00001000d00000097sv00001BD4sd00000011*
  ID_MODEL_FROM_DATABASE=SAS3008 PCI-Express Fusion-MPT SAS-3 (Inspur 12Gb 8i-3008 IT SAS HBA)
 
+pci:v00001000d00000097sv00001BD4sd00000012*
+ ID_MODEL_FROM_DATABASE=SAS3008 PCI-Express Fusion-MPT SAS-3 (12Gb SAS3008IR UDM)
+
+pci:v00001000d00000097sv00001BD4sd00000026*
+ ID_MODEL_FROM_DATABASE=SAS3008 PCI-Express Fusion-MPT SAS-3 (12G SAS3008IT RACK)
+
+pci:v00001000d00000097sv00001BD4sd00000027*
+ ID_MODEL_FROM_DATABASE=SAS3008 PCI-Express Fusion-MPT SAS-3 (12G SAS3008IMR RACK)
+
+pci:v00001000d00000097sv00001BD4sd00000028*
+ ID_MODEL_FROM_DATABASE=SAS3008 PCI-Express Fusion-MPT SAS-3 (12G SAS3008IR RACK)
+
 pci:v00001000d000000AB*
  ID_MODEL_FROM_DATABASE=SAS3516 Fusion-MPT Tri-Mode RAID On Chip (ROC)
 
@@ -2474,9 +2522,15 @@ pci:v00001002d00001307*
 pci:v00001002d00001308*
  ID_MODEL_FROM_DATABASE=Kaveri HDMI/DP Audio Controller
 
+pci:v00001002d00001308sv000017AAsd00003988*
+ ID_MODEL_FROM_DATABASE=Kaveri HDMI/DP Audio Controller (Z50-75)
+
 pci:v00001002d00001309*
  ID_MODEL_FROM_DATABASE=Kaveri [Radeon R6/R7 Graphics]
 
+pci:v00001002d00001309sv000017AAsd00003830*
+ ID_MODEL_FROM_DATABASE=Kaveri [Radeon R6/R7 Graphics] (Z50-75)
+
 pci:v00001002d0000130A*
  ID_MODEL_FROM_DATABASE=Kaveri [Radeon R6 Graphics]
 
@@ -2564,6 +2618,9 @@ pci:v00001002d000015D8*
 pci:v00001002d000015D8sv0000103Csd00008615*
  ID_MODEL_FROM_DATABASE=Picasso (Pavilion Laptop 15-cw1xxx)
 
+pci:v00001002d000015D8sv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=Picasso (ThinkPad E595)
+
 pci:v00001002d000015DD*
  ID_MODEL_FROM_DATABASE=Raven Ridge [Radeon Vega Series / Radeon Vega Mobile Series]
 
@@ -2579,6 +2636,9 @@ pci:v00001002d000015DE*
 pci:v00001002d000015DEsv0000103Csd00008615*
  ID_MODEL_FROM_DATABASE=Raven/Raven2/Fenghuang HDMI/DP Audio Controller (Pavilion Laptop 15-cw1xxx)
 
+pci:v00001002d000015DEsv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=Raven/Raven2/Fenghuang HDMI/DP Audio Controller (ThinkPad E595)
+
 pci:v00001002d000015DF*
  ID_MODEL_FROM_DATABASE=Raven/Raven2/Fenghuang/Renoir Cryptographic Coprocessor
 
@@ -3108,7 +3168,7 @@ pci:v00001002d00004383sv00001019sd00002120*
  ID_MODEL_FROM_DATABASE=SBx00 Azalia (Intel HDA) (A785GM-M)
 
 pci:v00001002d00004383sv0000103Csd00001611*
- ID_MODEL_FROM_DATABASE=SBx00 Azalia (Intel HDA) (Pavilion DM1Z-3000)
+ ID_MODEL_FROM_DATABASE=SBx00 Azalia (Intel HDA) (Pavilion dm1z-3000)
 
 pci:v00001002d00004383sv0000103Csd0000280A*
  ID_MODEL_FROM_DATABASE=SBx00 Azalia (Intel HDA) (DC5750 Microtower)
@@ -3329,6 +3389,9 @@ pci:v00001002d00004390sv00001849sd00004390*
 pci:v00001002d00004391*
  ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 SATA Controller [AHCI mode]
 
+pci:v00001002d00004391sv0000103Csd00001609*
+ ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 SATA Controller [AHCI mode] (ProLiant MicroServer N36L)
+
 pci:v00001002d00004391sv0000103Csd00001611*
  ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 SATA Controller [AHCI mode] (Pavilion DM1Z-3000)
 
@@ -3368,6 +3431,9 @@ pci:v00001002d00004396*
 pci:v00001002d00004396sv00001019sd00002120*
  ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB EHCI Controller (A785GM-M)
 
+pci:v00001002d00004396sv0000103Csd00001609*
+ ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB EHCI Controller (ProLiant MicroServer N36L)
+
 pci:v00001002d00004396sv0000103Csd00001611*
  ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB EHCI Controller (Pavilion DM1Z-3000)
 
@@ -3392,6 +3458,9 @@ pci:v00001002d00004397*
 pci:v00001002d00004397sv00001019sd00002120*
  ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB OHCI0 Controller (A785GM-M)
 
+pci:v00001002d00004397sv0000103Csd00001609*
+ ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB OHCI0 Controller (ProLiant MicroServer N36L)
+
 pci:v00001002d00004397sv0000103Csd00001611*
  ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 USB OHCI0 Controller (Pavilion DM1Z-3000)
 
@@ -3452,6 +3521,9 @@ pci:v00001002d0000439Csv00001002sd00004392*
 pci:v00001002d0000439Csv00001019sd00002120*
  ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 IDE Controller (A785GM-M)
 
+pci:v00001002d0000439Csv0000103Csd00001609*
+ ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 IDE Controller (ProLiant MicroServer N36L)
+
 pci:v00001002d0000439Csv00001043sd000082EF*
  ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 IDE Controller (M3A78-EH Motherboard)
 
@@ -3464,6 +3536,9 @@ pci:v00001002d0000439D*
 pci:v00001002d0000439Dsv00001019sd00002120*
  ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 LPC host controller (A785GM-M)
 
+pci:v00001002d0000439Dsv0000103Csd00001609*
+ ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 LPC host controller (ProLiant MicroServer N36L)
+
 pci:v00001002d0000439Dsv0000103Csd00001611*
  ID_MODEL_FROM_DATABASE=SB7x0/SB8x0/SB9x0 LPC host controller (Pavilion DM1Z-3000)
 
@@ -5391,22 +5466,25 @@ pci:v00001002d00006610sv00001642sd00003F09*
  ID_MODEL_FROM_DATABASE=Oland XT [Radeon HD 8670 / R7 250/350] (Radeon R7 350)
 
 pci:v00001002d00006611*
- ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 OEM]
+ ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 / Radeon 520 OEM]
 
 pci:v00001002d00006611sv00001028sd0000210B*
- ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 OEM] (Radeon R5 240 OEM)
+ ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 / Radeon 520 OEM] (Radeon R5 240 OEM)
+
+pci:v00001002d00006611sv00001642sd00001869*
+ ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 / Radeon 520 OEM] (Radeon 520 OEM)
 
 pci:v00001002d00006611sv0000174Bsd00004248*
- ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 OEM] (Radeon R7 240 OEM)
+ ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 / Radeon 520 OEM] (Radeon R7 240 OEM)
 
 pci:v00001002d00006611sv0000174Bsd0000A240*
- ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 OEM] (Radeon R7 240 OEM)
+ ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 / Radeon 520 OEM] (Radeon R7 240 OEM)
 
 pci:v00001002d00006611sv0000174Bsd0000D340*
- ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 OEM] (Radeon R7 340 OEM)
+ ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 / Radeon 520 OEM] (Radeon R7 340 OEM)
 
 pci:v00001002d00006611sv00001B0Asd000090D3*
- ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 OEM] (Radeon R7 240 OEM)
+ ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R7 240/340 / Radeon 520 OEM] (Radeon R7 240 OEM)
 
 pci:v00001002d00006613*
  ID_MODEL_FROM_DATABASE=Oland PRO [Radeon R7 240/340]
@@ -5583,7 +5661,7 @@ pci:v00001002d00006665*
  ID_MODEL_FROM_DATABASE=Jet PRO [Radeon R5 M230 / R7 M260DX / Radeon 520 Mobile]
 
 pci:v00001002d00006665sv000017AAsd00001309*
- ID_MODEL_FROM_DATABASE=Jet PRO [Radeon R5 M230 / R7 M260DX / Radeon 520 Mobile] (Radeon R7 M260DX)
+ ID_MODEL_FROM_DATABASE=Jet PRO [Radeon R5 M230 / R7 M260DX / Radeon 520 Mobile] (Z50-75 Radeon R7 M260DX)
 
 pci:v00001002d00006665sv000017AAsd0000368F*
  ID_MODEL_FROM_DATABASE=Jet PRO [Radeon R5 M230 / R7 M260DX / Radeon 520 Mobile] (Radeon R5 A230)
@@ -7464,7 +7542,7 @@ pci:v00001002d000067DFsv00001462sd00008A92*
  ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 580)
 
 pci:v00001002d000067DFsv0000148Csd00002372*
- ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 480)
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 480 [Red Dragon])
 
 pci:v00001002d000067DFsv0000148Csd00002373*
  ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 470)
@@ -7496,6 +7574,9 @@ pci:v00001002d000067DFsv00001787sd0000A480*
 pci:v00001002d000067DFsv00001849sd00005001*
  ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Phantom Gaming X RX 580 OC)
 
+pci:v00001002d000067DFsv00001849sd00005030*
+ ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Phantom Gaming D Radeon RX580 8G OC)
+
 pci:v00001002d000067DFsv00001DA2sd0000E353*
  ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 570 Pulse 4GB)
 
@@ -10307,11 +10388,23 @@ pci:v00001002d00007300sv0000174Bsd0000E329*
 pci:v00001002d00007310*
  ID_MODEL_FROM_DATABASE=Navi 10
 
+pci:v00001002d00007312*
+ ID_MODEL_FROM_DATABASE=Navi 10 [Radeon Pro W5700]
+
 pci:v00001002d0000731F*
- ID_MODEL_FROM_DATABASE=Navi 10 [Radeon RX 5700 / 5700 XT]
+ ID_MODEL_FROM_DATABASE=Navi 10 [Radeon RX 5600 OEM/5600 XT / 5700/5700 XT]
 
 pci:v00001002d00007340*
- ID_MODEL_FROM_DATABASE=Navi 14 [Radeon RX 5500 / 5500M]
+ ID_MODEL_FROM_DATABASE=Navi 14 [Radeon RX 5500/5500M / Pro 5500M]
+
+pci:v00001002d00007341*
+ ID_MODEL_FROM_DATABASE=Navi 14 [Radeon Pro W5500]
+
+pci:v00001002d00007347*
+ ID_MODEL_FROM_DATABASE=Navi 14 [Radeon Pro W5500M]
+
+pci:v00001002d0000734F*
+ ID_MODEL_FROM_DATABASE=Navi 14 [Radeon Pro W5300M]
 
 pci:v00001002d00007833*
  ID_MODEL_FROM_DATABASE=RS350 Host Bridge
@@ -11003,6 +11096,9 @@ pci:v00001002d00009710sv00001043sd000083A2*
 pci:v00001002d00009712*
  ID_MODEL_FROM_DATABASE=RS880M [Mobility Radeon HD 4225/4250]
 
+pci:v00001002d00009712sv0000103Csd00001609*
+ ID_MODEL_FROM_DATABASE=RS880M [Mobility Radeon HD 4225/4250] (ProLiant MicroServer N36L)
+
 pci:v00001002d00009713*
  ID_MODEL_FROM_DATABASE=RS880M [Mobility Radeon HD 4100]
 
@@ -11054,6 +11150,9 @@ pci:v00001002d00009831*
 pci:v00001002d00009832*
  ID_MODEL_FROM_DATABASE=Kabini [Radeon HD 8330]
 
+pci:v00001002d00009832sv00001849sd00009832*
+ ID_MODEL_FROM_DATABASE=Kabini [Radeon HD 8330] (QC5000-ITX/PH)
+
 pci:v00001002d00009833*
  ID_MODEL_FROM_DATABASE=Kabini [Radeon HD 8330E]
 
@@ -11081,6 +11180,9 @@ pci:v00001002d0000983D*
 pci:v00001002d00009840*
  ID_MODEL_FROM_DATABASE=Kabini HDMI/DP Audio
 
+pci:v00001002d00009840sv00001849sd00009840*
+ ID_MODEL_FROM_DATABASE=Kabini HDMI/DP Audio (QC5000-ITX/PH)
+
 pci:v00001002d00009850*
  ID_MODEL_FROM_DATABASE=Mullins [Radeon R3 Graphics]
 
@@ -13367,6 +13469,9 @@ pci:v00001022d00001468*
 pci:v00001022d00001480*
  ID_MODEL_FROM_DATABASE=Starship/Matisse Root Complex
 
+pci:v00001022d00001480sv00001462sd00007C37*
+ ID_MODEL_FROM_DATABASE=Starship/Matisse Root Complex (X570-A PRO motherboard)
+
 pci:v00001022d00001481*
  ID_MODEL_FROM_DATABASE=Starship/Matisse IOMMU
 
@@ -13388,6 +13493,9 @@ pci:v00001022d00001486*
 pci:v00001022d00001487*
  ID_MODEL_FROM_DATABASE=Starship/Matisse HD Audio Controller
 
+pci:v00001022d00001487sv00001462sd00009C37*
+ ID_MODEL_FROM_DATABASE=Starship/Matisse HD Audio Controller (X570-A PRO motherboard)
+
 pci:v00001022d00001488*
  ID_MODEL_FROM_DATABASE=Starship Reserved SSP
 
@@ -13451,6 +13559,9 @@ pci:v00001022d0000149B*
 pci:v00001022d0000149C*
  ID_MODEL_FROM_DATABASE=Matisse USB 3.0 Host Controller
 
+pci:v00001022d0000149Csv00001462sd00007C37*
+ ID_MODEL_FROM_DATABASE=Matisse USB 3.0 Host Controller (X570-A PRO motherboard)
+
 pci:v00001022d00001510*
  ID_MODEL_FROM_DATABASE=Family 14h Processor Root Complex
 
@@ -13493,6 +13604,9 @@ pci:v00001022d00001535*
 pci:v00001022d00001536*
  ID_MODEL_FROM_DATABASE=Family 16h Processor Root Complex
 
+pci:v00001022d00001536sv00001849sd00001536*
+ ID_MODEL_FROM_DATABASE=Family 16h Processor Root Complex (QC5000-ITX/PH)
+
 pci:v00001022d00001537*
  ID_MODEL_FROM_DATABASE=Kabini/Mullins PSP-Platform Security Processor
 
@@ -13733,27 +13847,42 @@ pci:v00001022d000015DE*
 pci:v00001022d000015DF*
  ID_MODEL_FROM_DATABASE=Family 17h (Models 10h-1fh) Platform Security Processor
 
+pci:v00001022d000015DFsv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=Family 17h (Models 10h-1fh) Platform Security Processor (ThinkPad E595)
+
 pci:v00001022d000015E0*
  ID_MODEL_FROM_DATABASE=Raven USB 3.1
 
 pci:v00001022d000015E0sv0000103Csd00008615*
  ID_MODEL_FROM_DATABASE=Raven USB 3.1 (Pavilion Laptop 15-cw1xxx)
 
+pci:v00001022d000015E0sv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=Raven USB 3.1 (ThinkPad E595)
+
 pci:v00001022d000015E1*
  ID_MODEL_FROM_DATABASE=Raven USB 3.1
 
 pci:v00001022d000015E1sv0000103Csd00008615*
  ID_MODEL_FROM_DATABASE=Raven USB 3.1 (Pavilion Laptop 15-cw1xxx)
 
+pci:v00001022d000015E1sv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=Raven USB 3.1 (ThinkPad E595)
+
 pci:v00001022d000015E2*
  ID_MODEL_FROM_DATABASE=Raven/Raven2/FireFlight/Renoir Audio Processor
 
+pci:v00001022d000015E2sv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=Raven/Raven2/FireFlight/Renoir Audio Processor (ThinkPad E595)
+
 pci:v00001022d000015E3*
  ID_MODEL_FROM_DATABASE=Family 17h (Models 10h-1fh) HD Audio Controller
 
 pci:v00001022d000015E3sv0000103Csd00008615*
  ID_MODEL_FROM_DATABASE=Family 17h (Models 10h-1fh) HD Audio Controller (Pavilion Laptop 15-cw1xxx)
 
+pci:v00001022d000015E3sv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=Family 17h (Models 10h-1fh) HD Audio Controller (ThinkPad E595)
+
 pci:v00001022d000015E4*
  ID_MODEL_FROM_DATABASE=Raven/Raven2/Renoir Sensor Fusion Hub
 
@@ -14195,6 +14324,15 @@ pci:v00001022d000043C8*
 pci:v00001022d000043D5*
  ID_MODEL_FROM_DATABASE=400 Series Chipset USB 3.1 XHCI Controller
 
+pci:v00001022d000057A3*
+ ID_MODEL_FROM_DATABASE=Matisse PCIe GPP Bridge
+
+pci:v00001022d000057A4*
+ ID_MODEL_FROM_DATABASE=Matisse PCIe GPP Bridge
+
+pci:v00001022d000057AD*
+ ID_MODEL_FROM_DATABASE=Matisse Switch Upstream
+
 pci:v00001022d00007006*
  ID_MODEL_FROM_DATABASE=AMD-751 [Irongate] System Controller
 
@@ -14369,6 +14507,12 @@ pci:v00001022d00007801sv0000103Csd0000168B*
 pci:v00001022d00007801sv0000103Csd0000194E*
  ID_MODEL_FROM_DATABASE=FCH SATA Controller [AHCI mode] (ProBook 455 G1 Notebook)
 
+pci:v00001022d00007801sv000017AAsd00003988*
+ ID_MODEL_FROM_DATABASE=FCH SATA Controller [AHCI mode] (Z50-75)
+
+pci:v00001022d00007801sv00001849sd00007801*
+ ID_MODEL_FROM_DATABASE=FCH SATA Controller [AHCI mode] (QC5000-ITX/PH)
+
 pci:v00001022d00007802*
  ID_MODEL_FROM_DATABASE=FCH SATA Controller [RAID mode]
 
@@ -14396,6 +14540,12 @@ pci:v00001022d00007807sv0000103Csd0000194E*
 pci:v00001022d00007807sv0000103Csd00001985*
  ID_MODEL_FROM_DATABASE=FCH USB OHCI Controller (Pavilion 17-e163sg Notebook PC)
 
+pci:v00001022d00007807sv000017AAsd00003988*
+ ID_MODEL_FROM_DATABASE=FCH USB OHCI Controller (Z50-75)
+
+pci:v00001022d00007807sv00001849sd00007807*
+ ID_MODEL_FROM_DATABASE=FCH USB OHCI Controller (QC5000-ITX/PH)
+
 pci:v00001022d00007808*
  ID_MODEL_FROM_DATABASE=FCH USB EHCI Controller
 
@@ -14405,12 +14555,21 @@ pci:v00001022d00007808sv0000103Csd0000194E*
 pci:v00001022d00007808sv0000103Csd00001985*
  ID_MODEL_FROM_DATABASE=FCH USB EHCI Controller (Pavilion 17-e163sg Notebook PC)
 
+pci:v00001022d00007808sv000017AAsd00003988*
+ ID_MODEL_FROM_DATABASE=FCH USB EHCI Controller (Z50-75)
+
+pci:v00001022d00007808sv00001849sd00007808*
+ ID_MODEL_FROM_DATABASE=FCH USB EHCI Controller (QC5000-ITX/PH)
+
 pci:v00001022d00007809*
  ID_MODEL_FROM_DATABASE=FCH USB OHCI Controller
 
 pci:v00001022d00007809sv0000103Csd0000194E*
  ID_MODEL_FROM_DATABASE=FCH USB OHCI Controller (ProBook 455 G1 Notebook)
 
+pci:v00001022d00007809sv000017AAsd00003988*
+ ID_MODEL_FROM_DATABASE=FCH USB OHCI Controller (Z50-75)
+
 pci:v00001022d0000780A*
  ID_MODEL_FROM_DATABASE=Kabini/Mullins SATA Raid/AHCI Mode (DotHill driver)
 
@@ -14423,6 +14582,12 @@ pci:v00001022d0000780Bsv0000103Csd0000194E*
 pci:v00001022d0000780Bsv0000103Csd00001985*
  ID_MODEL_FROM_DATABASE=FCH SMBus Controller (Pavilion 17-e163sg Notebook PC)
 
+pci:v00001022d0000780Bsv000017AAsd00003988*
+ ID_MODEL_FROM_DATABASE=FCH SMBus Controller (Z50-75)
+
+pci:v00001022d0000780Bsv00001849sd0000780B*
+ ID_MODEL_FROM_DATABASE=FCH SMBus Controller (QC5000-ITX/PH)
+
 pci:v00001022d0000780C*
  ID_MODEL_FROM_DATABASE=FCH IDE Controller
 
@@ -14438,6 +14603,12 @@ pci:v00001022d0000780Dsv0000103Csd00001985*
 pci:v00001022d0000780Dsv00001043sd00008444*
  ID_MODEL_FROM_DATABASE=FCH Azalia Controller (F2A85-M Series)
 
+pci:v00001022d0000780Dsv000017AAsd00003988*
+ ID_MODEL_FROM_DATABASE=FCH Azalia Controller (Z50-75)
+
+pci:v00001022d0000780Dsv00001849sd00008892*
+ ID_MODEL_FROM_DATABASE=FCH Azalia Controller (QC5000-ITX/PH)
+
 pci:v00001022d0000780E*
  ID_MODEL_FROM_DATABASE=FCH LPC Bridge
 
@@ -14447,6 +14618,12 @@ pci:v00001022d0000780Esv0000103Csd0000194E*
 pci:v00001022d0000780Esv0000103Csd00001985*
  ID_MODEL_FROM_DATABASE=FCH LPC Bridge (Pavilion 17-e163sg Notebook PC)
 
+pci:v00001022d0000780Esv000017AAsd00003988*
+ ID_MODEL_FROM_DATABASE=FCH LPC Bridge (Z50-75)
+
+pci:v00001022d0000780Esv00001849sd0000780E*
+ ID_MODEL_FROM_DATABASE=FCH LPC Bridge (QC5000-ITX/PH)
+
 pci:v00001022d0000780F*
  ID_MODEL_FROM_DATABASE=FCH PCI Bridge
 
@@ -14465,6 +14642,12 @@ pci:v00001022d00007814sv0000103Csd0000194E*
 pci:v00001022d00007814sv0000103Csd00001985*
  ID_MODEL_FROM_DATABASE=FCH USB XHCI Controller (Pavilion 17-e163sg Notebook PC)
 
+pci:v00001022d00007814sv000017AAsd00003988*
+ ID_MODEL_FROM_DATABASE=FCH USB XHCI Controller (Z50-75)
+
+pci:v00001022d00007814sv00001849sd00007814*
+ ID_MODEL_FROM_DATABASE=FCH USB XHCI Controller (QC5000-ITX/PH)
+
 pci:v00001022d00007900*
  ID_MODEL_FROM_DATABASE=FCH SATA Controller [IDE mode]
 
@@ -14474,6 +14657,9 @@ pci:v00001022d00007901*
 pci:v00001022d00007901sv0000103Csd00008615*
  ID_MODEL_FROM_DATABASE=FCH SATA Controller [AHCI mode] (Pavilion Laptop 15-cw1xxx)
 
+pci:v00001022d00007901sv00001462sd00007C37*
+ ID_MODEL_FROM_DATABASE=FCH SATA Controller [AHCI mode] (X570-A PRO motherboard)
+
 pci:v00001022d00007902*
  ID_MODEL_FROM_DATABASE=FCH SATA Controller [RAID mode]
 
@@ -14495,12 +14681,24 @@ pci:v00001022d0000790B*
 pci:v00001022d0000790Bsv0000103Csd00008615*
  ID_MODEL_FROM_DATABASE=FCH SMBus Controller (Pavilion Laptop 15-cw1xxx)
 
+pci:v00001022d0000790Bsv00001462sd00007C37*
+ ID_MODEL_FROM_DATABASE=FCH SMBus Controller (X570-A PRO motherboard)
+
+pci:v00001022d0000790Bsv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=FCH SMBus Controller (ThinkPad E595)
+
 pci:v00001022d0000790E*
  ID_MODEL_FROM_DATABASE=FCH LPC Bridge
 
 pci:v00001022d0000790Esv0000103Csd00008615*
  ID_MODEL_FROM_DATABASE=FCH LPC Bridge (Pavilion Laptop 15-cw1xxx)
 
+pci:v00001022d0000790Esv00001462sd00007C37*
+ ID_MODEL_FROM_DATABASE=FCH LPC Bridge (X570-A PRO motherboard)
+
+pci:v00001022d0000790Esv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=FCH LPC Bridge (ThinkPad E595)
+
 pci:v00001022d0000790F*
  ID_MODEL_FROM_DATABASE=FCH PCI Bridge
 
@@ -14522,6 +14720,9 @@ pci:v00001022d00009601*
 pci:v00001022d00009601sv00001019sd00002120*
  ID_MODEL_FROM_DATABASE=RS880 Host Bridge (A785GM-M)
 
+pci:v00001022d00009601sv0000103Csd00001609*
+ ID_MODEL_FROM_DATABASE=RS880 Host Bridge (ProLiant MicroServer N36L)
+
 pci:v00001022d00009601sv00001043sd000083A2*
  ID_MODEL_FROM_DATABASE=RS880 Host Bridge (M4A785-M Mainboard)
 
@@ -14534,6 +14735,9 @@ pci:v00001022d00009602*
 pci:v00001022d00009603*
  ID_MODEL_FROM_DATABASE=RS780 PCI to PCI bridge (ext gfx port 0)
 
+pci:v00001022d00009603sv0000103Csd00001609*
+ ID_MODEL_FROM_DATABASE=RS780 PCI to PCI bridge (ext gfx port 0) (ProLiant MicroServer N36L)
+
 pci:v00001022d00009604*
  ID_MODEL_FROM_DATABASE=RS780/RS880 PCI to PCI bridge (PCIE port 0)
 
@@ -14543,6 +14747,9 @@ pci:v00001022d00009605*
 pci:v00001022d00009606*
  ID_MODEL_FROM_DATABASE=RS780 PCI to PCI bridge (PCIE port 2)
 
+pci:v00001022d00009606sv0000103Csd00001609*
+ ID_MODEL_FROM_DATABASE=RS780 PCI to PCI bridge (PCIE port 2) (ProLiant MicroServer N36L)
+
 pci:v00001022d00009607*
  ID_MODEL_FROM_DATABASE=RS780/RS880 PCI to PCI bridge (PCIE port 3)
 
@@ -17384,6 +17591,9 @@ pci:v0000103Cd00004037*
 pci:v0000103Cd00009602*
  ID_MODEL_FROM_DATABASE=AMD RS780/RS880 PCI to PCI bridge (int gfx)
 
+pci:v0000103Cd00009602sv0000103Csd00001609*
+ ID_MODEL_FROM_DATABASE=AMD RS780/RS880 PCI to PCI bridge (int gfx) (ProLiant MicroServer N36L)
+
 pci:v0000103E*
  ID_VENDOR_FROM_DATABASE=Solliday Engineering
 
@@ -19118,6 +19328,9 @@ pci:v00001057d00004803*
 pci:v00001057d00004806*
  ID_MODEL_FROM_DATABASE=CPX8216
 
+pci:v00001057d0000480B*
+ ID_MODEL_FROM_DATABASE=MPC7410
+
 pci:v00001057d00004D68*
  ID_MODEL_FROM_DATABASE=20268
 
@@ -20405,6 +20618,12 @@ pci:v00001077d00002281sv00001077sd000002EE*
 pci:v00001077d00002281sv00001077sd000002F0*
  ID_MODEL_FROM_DATABASE=ISP2812-based 64/32G Fibre Channel to PCIe Controller (QLE2770 Single Port 32GFC PCIe Gen4 x8 Adapter)
 
+pci:v00001077d00002281sv00001077sd000002F2*
+ ID_MODEL_FROM_DATABASE=ISP2812-based 64/32G Fibre Channel to PCIe Controller (QLogic 1x32Gb QLE2770 FC HBA)
+
+pci:v00001077d00002281sv00001077sd000002F3*
+ ID_MODEL_FROM_DATABASE=ISP2812-based 64/32G Fibre Channel to PCIe Controller (QLogic 2x32Gb QLE2772 FC HBA)
+
 pci:v00001077d00002281sv00001590sd000002D3*
  ID_MODEL_FROM_DATABASE=ISP2812-based 64/32G Fibre Channel to PCIe Controller (SN1610Q - 1P Enhanced 32GFC Single Port Fibre Channel Host Bus Adapter)
 
@@ -20639,6 +20858,33 @@ pci:v00001077d00008070sv00001077sd00000056*
 pci:v00001077d00008070sv00001077sd00000057*
  ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (2x25GE QL41232HxCU NIC)
 
+pci:v00001077d00008070sv00001077sd00000065*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (QLogic 4x10GE QL41154HQRJ CNA)
+
+pci:v00001077d00008070sv00001077sd00000066*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (QLogic 4x10GE QL41154HQCU CNA)
+
+pci:v00001077d00008070sv00001077sd00000068*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (10GbE 2p SFP+ QL41132HLCU-HC Adapter)
+
+pci:v00001077d00008070sv00001077sd00000069*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (10GbE 2p BASE-T QL41132HQRJ-HC OCP3 Adapter)
+
+pci:v00001077d00008070sv00001077sd00000070*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (10GbE 2p BASE-T QL41132HLRJ-HC Adapter)
+
+pci:v00001077d00008070sv00001077sd00000071*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (10GbE 2p SFP+ QL41132HQCU-HC OCP3 Adapter)
+
+pci:v00001077d00008070sv00001077sd00000072*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (10GbE 4p SFP+ QL41134HLCU-HC Adapter)
+
+pci:v00001077d00008070sv00001077sd00000073*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (10/25GbE 2p SFP28 QL41232HQCU-HC OCP3 Adapter)
+
+pci:v00001077d00008070sv00001077sd00000074*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (10/25GbE 2p SFP28 QL41232HLCU-HC Adapter)
+
 pci:v00001077d00008070sv00001590sd0000021A*
  ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (10GbE 2P QL41162HLRJ-HP Adapter)
 
@@ -20747,6 +20993,12 @@ pci:v00001077d00008084sv00001077sd0000000E*
 pci:v00001077d00008084sv00001077sd0000000F*
  ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (iSCSI) (2x25GE QL41262HMKR CNA)
 
+pci:v00001077d00008084sv00001077sd00000065*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (iSCSI) (QLogic 4x10GE QL41154HQRJ CNA)
+
+pci:v00001077d00008084sv00001077sd00000066*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (iSCSI) (QLogic 4x10GE QL41154HQCU CNA)
+
 pci:v00001077d00008084sv00001590sd0000021A*
  ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series 10/25/40/50GbE Controller (iSCSI) (10GbE 2P QL41162HLRJ-HP Adapter)
 
@@ -20819,6 +21071,12 @@ pci:v00001077d00008090sv00001077sd00000056*
 pci:v00001077d00008090sv00001077sd00000057*
  ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF) (2x25GE QL41232HxCU NIC)
 
+pci:v00001077d00008090sv00001077sd00000065*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF) (QLogic 4x10GE QL41154HQRJ CNA)
+
+pci:v00001077d00008090sv00001077sd00000066*
+ ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF) (QLogic 4x10GE QL41154HQCU CNA)
+
 pci:v00001077d00008090sv00001590sd0000021A*
  ID_MODEL_FROM_DATABASE=FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF) (10GbE 2P QL41162HLRJ-HP Adapter)
 
@@ -32297,6 +32555,9 @@ pci:v000010DEd00000FBA*
 pci:v000010DEd00000FBB*
  ID_MODEL_FROM_DATABASE=GM204 High Definition Audio Controller
 
+pci:v000010DEd00000FBC*
+ ID_MODEL_FROM_DATABASE=GM107 High Definition Audio Controller [GeForce 940MX]
+
 pci:v000010DEd00000FC0*
  ID_MODEL_FROM_DATABASE=GK107 [GeForce GT 640 OEM]
 
@@ -32813,6 +33074,9 @@ pci:v000010DEd000010F1*
 pci:v000010DEd000010F7*
  ID_MODEL_FROM_DATABASE=TU102 High Definition Audio Controller
 
+pci:v000010DEd000010F8*
+ ID_MODEL_FROM_DATABASE=TU104 HD Audio Controller
+
 pci:v000010DEd000010F9*
  ID_MODEL_FROM_DATABASE=TU106 High Definition Audio Controller
 
@@ -34595,6 +34859,9 @@ pci:v000010DEd00001407*
 pci:v000010DEd00001427*
  ID_MODEL_FROM_DATABASE=GM206M [GeForce GTX 965M]
 
+pci:v000010DEd00001427sv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=GM206M [GeForce GTX 965M] (OMEN-17-w001nv)
+
 pci:v000010DEd00001430*
  ID_MODEL_FROM_DATABASE=GM206GL [Quadro M2000]
 
@@ -34655,6 +34922,9 @@ pci:v000010DEd00001789*
 pci:v000010DEd0000179C*
  ID_MODEL_FROM_DATABASE=GM107 [GeForce 940MX]
 
+pci:v000010DEd0000179Csv00001025sd00001094*
+ ID_MODEL_FROM_DATABASE=GM107 [GeForce 940MX] (Acer Aspire E5-575G)
+
 pci:v000010DEd000017C2*
  ID_MODEL_FROM_DATABASE=GM200 [GeForce GTX TITAN X]
 
@@ -34700,6 +34970,9 @@ pci:v000010DEd00001ADBsv00001043sd00008673*
 pci:v000010DEd00001AEB*
  ID_MODEL_FROM_DATABASE=TU116 High Definition Audio Controller
 
+pci:v000010DEd00001AED*
+ ID_MODEL_FROM_DATABASE=TU116 [GeForce GTX 1650 SUPER]
+
 pci:v000010DEd00001B00*
  ID_MODEL_FROM_DATABASE=GP102 [TITAN X]
 
@@ -34883,6 +35156,9 @@ pci:v000010DEd00001C2D*
 pci:v000010DEd00001C30*
  ID_MODEL_FROM_DATABASE=GP106GL [Quadro P2000]
 
+pci:v000010DEd00001C31*
+ ID_MODEL_FROM_DATABASE=GP106GL [Quadro P2200]
+
 pci:v000010DEd00001C35*
  ID_MODEL_FROM_DATABASE=GP106
 
@@ -34907,6 +35183,12 @@ pci:v000010DEd00001C81*
 pci:v000010DEd00001C82*
  ID_MODEL_FROM_DATABASE=GP107 [GeForce GTX 1050 Ti]
 
+pci:v000010DEd00001C82sv00001043sd00008613*
+ ID_MODEL_FROM_DATABASE=GP107 [GeForce GTX 1050 Ti] (PH-GTX1050TI-4G)
+
+pci:v000010DEd00001C82sv00001458sd00003763*
+ ID_MODEL_FROM_DATABASE=GP107 [GeForce GTX 1050 Ti] (GV-N105TOC-4GD)
+
 pci:v000010DEd00001C83*
  ID_MODEL_FROM_DATABASE=GP107 [GeForce GTX 1050 3GB]
 
@@ -34931,6 +35213,9 @@ pci:v000010DEd00001C91*
 pci:v000010DEd00001C92*
  ID_MODEL_FROM_DATABASE=GP107M [GeForce GTX 1050 Mobile]
 
+pci:v000010DEd00001C94*
+ ID_MODEL_FROM_DATABASE=GP107M [GeForce MX350]
+
 pci:v000010DEd00001CA7*
  ID_MODEL_FROM_DATABASE=GP107GL
 
@@ -34979,12 +35264,21 @@ pci:v000010DEd00001CBBsv0000103Csd00008451*
 pci:v000010DEd00001CBC*
  ID_MODEL_FROM_DATABASE=GP107GLM [Quadro P600 Mobile]
 
+pci:v000010DEd00001CBD*
+ ID_MODEL_FROM_DATABASE=GP107GLM [Quadro P620]
+
 pci:v000010DEd00001CCC*
  ID_MODEL_FROM_DATABASE=GP107BM [GeForce GTX 1050 Ti Mobile]
 
 pci:v000010DEd00001CCD*
  ID_MODEL_FROM_DATABASE=GP107BM [GeForce GTX 1050 Mobile]
 
+pci:v000010DEd00001CFA*
+ ID_MODEL_FROM_DATABASE=GP107GL [Quadro P2000]
+
+pci:v000010DEd00001CFB*
+ ID_MODEL_FROM_DATABASE=GP107GL [Quadro P1000]
+
 pci:v000010DEd00001D01*
  ID_MODEL_FROM_DATABASE=GP108 [GeForce GT 1030]
 
@@ -35006,9 +35300,15 @@ pci:v000010DEd00001D12sv00001D72sd00001701*
 pci:v000010DEd00001D13*
  ID_MODEL_FROM_DATABASE=GP108M [GeForce MX250]
 
+pci:v000010DEd00001D16*
+ ID_MODEL_FROM_DATABASE=GP108M [GeForce MX330]
+
 pci:v000010DEd00001D33*
  ID_MODEL_FROM_DATABASE=GP108GLM [Quadro P500 Mobile]
 
+pci:v000010DEd00001D34*
+ ID_MODEL_FROM_DATABASE=GP108GLM [Quadro P520]
+
 pci:v000010DEd00001D52*
  ID_MODEL_FROM_DATABASE=GP108BM [GeForce MX250]
 
@@ -35019,7 +35319,7 @@ pci:v000010DEd00001DB1*
  ID_MODEL_FROM_DATABASE=GV100GL [Tesla V100 SXM2 16GB]
 
 pci:v000010DEd00001DB2*
- ID_MODEL_FROM_DATABASE=GV100GL [Tesla V100-DGXS-16GB]
+ ID_MODEL_FROM_DATABASE=GV100GL [Tesla V100 DGXS 16GB]
 
 pci:v000010DEd00001DB3*
  ID_MODEL_FROM_DATABASE=GV100GL [Tesla V100 FHHL 16GB]
@@ -35036,12 +35336,30 @@ pci:v000010DEd00001DB6*
 pci:v000010DEd00001DB7*
  ID_MODEL_FROM_DATABASE=GV100GL [Tesla V100 DGXS 32GB]
 
+pci:v000010DEd00001DB8*
+ ID_MODEL_FROM_DATABASE=GV100GL [Tesla V100 SXM3 32GB]
+
+pci:v000010DEd00001DB8sv000010DEsd0000131D*
+ ID_MODEL_FROM_DATABASE=GV100GL [Tesla V100 SXM3 32GB] (Tesla V100-SXM3-32GB-H)
+
 pci:v000010DEd00001DBA*
  ID_MODEL_FROM_DATABASE=GV100GL [Quadro GV100]
 
 pci:v000010DEd00001DBAsv000010DEsd000012EB*
  ID_MODEL_FROM_DATABASE=GV100GL [Quadro GV100] (TITAN V CEO Edition)
 
+pci:v000010DEd00001DF0*
+ ID_MODEL_FROM_DATABASE=GV100GL [Tesla PG500-216]
+
+pci:v000010DEd00001DF2*
+ ID_MODEL_FROM_DATABASE=GV100GL [Tesla PG503-216]
+
+pci:v000010DEd00001DF5*
+ ID_MODEL_FROM_DATABASE=GV100GL [Tesla V100 SXM2 16GB]
+
+pci:v000010DEd00001DF6*
+ ID_MODEL_FROM_DATABASE=GV100GL [Tesla V100S PCIe 32GB]
+
 pci:v000010DEd00001E02*
  ID_MODEL_FROM_DATABASE=TU102 [TITAN RTX]
 
@@ -35069,6 +35387,18 @@ pci:v000010DEd00001E30sv000010DEsd0000129E*
 pci:v000010DEd00001E30sv000010DEsd000012BA*
  ID_MODEL_FROM_DATABASE=TU102GL [Quadro RTX 6000/8000] (Quadro RTX 6000)
 
+pci:v000010DEd00001E37*
+ ID_MODEL_FROM_DATABASE=TU102GL [GRID RTX T10-4/T10-8/T10-16]
+
+pci:v000010DEd00001E37sv000010DEsd00001347*
+ ID_MODEL_FROM_DATABASE=TU102GL [GRID RTX T10-4/T10-8/T10-16] (GRID RTX T10-8)
+
+pci:v000010DEd00001E37sv000010DEsd00001348*
+ ID_MODEL_FROM_DATABASE=TU102GL [GRID RTX T10-4/T10-8/T10-16] (GRID RTX T10-4)
+
+pci:v000010DEd00001E37sv000010DEsd00001370*
+ ID_MODEL_FROM_DATABASE=TU102GL [GRID RTX T10-4/T10-8/T10-16] (GRID RTX T10-16)
+
 pci:v000010DEd00001E38*
  ID_MODEL_FROM_DATABASE=TU102GL
 
@@ -35081,6 +35411,15 @@ pci:v000010DEd00001E3D*
 pci:v000010DEd00001E3E*
  ID_MODEL_FROM_DATABASE=TU102GL
 
+pci:v000010DEd00001E78*
+ ID_MODEL_FROM_DATABASE=TU102GL [Quadro RTX 6000/8000]
+
+pci:v000010DEd00001E78sv000010DEsd000013D8*
+ ID_MODEL_FROM_DATABASE=TU102GL [Quadro RTX 6000/8000] (Quadro RTX 8000)
+
+pci:v000010DEd00001E78sv000010DEsd000013D9*
+ ID_MODEL_FROM_DATABASE=TU102GL [Quadro RTX 6000/8000] (Quadro RTX 6000)
+
 pci:v000010DEd00001E81*
  ID_MODEL_FROM_DATABASE=TU104 [GeForce RTX 2080 SUPER]
 
@@ -35093,6 +35432,9 @@ pci:v000010DEd00001E84*
 pci:v000010DEd00001E87*
  ID_MODEL_FROM_DATABASE=TU104 [GeForce RTX 2080 Rev. A]
 
+pci:v000010DEd00001E89*
+ ID_MODEL_FROM_DATABASE=TU104 [GeForce RTX 2060]
+
 pci:v000010DEd00001E90*
  ID_MODEL_FROM_DATABASE=TU104M [GeForce RTX 2080 Mobile]
 
@@ -35123,6 +35465,12 @@ pci:v000010DEd00001EB9*
 pci:v000010DEd00001EBE*
  ID_MODEL_FROM_DATABASE=TU104GL
 
+pci:v000010DEd00001EC2*
+ ID_MODEL_FROM_DATABASE=TU104 [GeForce RTX 2070 SUPER]
+
+pci:v000010DEd00001EC7*
+ ID_MODEL_FROM_DATABASE=TU104 [GeForce RTX 2070 SUPER]
+
 pci:v000010DEd00001ED0*
  ID_MODEL_FROM_DATABASE=TU104BM [GeForce RTX 2080 Mobile]
 
@@ -35156,6 +35504,12 @@ pci:v000010DEd00001F2E*
 pci:v000010DEd00001F36*
  ID_MODEL_FROM_DATABASE=TU106GLM [Quadro RTX 3000 Mobile / Max-Q]
 
+pci:v000010DEd00001F42*
+ ID_MODEL_FROM_DATABASE=TU106 [GeForce RTX 2060 SUPER]
+
+pci:v000010DEd00001F47*
+ ID_MODEL_FROM_DATABASE=TU106 [GeForce RTX 2060 SUPER]
+
 pci:v000010DEd00001F50*
  ID_MODEL_FROM_DATABASE=TU106BM [GeForce RTX 2070 Mobile]
 
@@ -35168,9 +35522,15 @@ pci:v000010DEd00001F81*
 pci:v000010DEd00001F82*
  ID_MODEL_FROM_DATABASE=TU117 [GeForce GTX 1650]
 
+pci:v000010DEd00001F91*
+ ID_MODEL_FROM_DATABASE=TU117M [GeForce GTX 1650 Mobile / Max-Q]
+
 pci:v000010DEd00001F92*
  ID_MODEL_FROM_DATABASE=TU117M [GeForce GTX 1650 Mobile]
 
+pci:v000010DEd00001F96*
+ ID_MODEL_FROM_DATABASE=TU117M [GeForce GTX 1650 Mobile / Max-Q]
+
 pci:v000010DEd00001FAE*
  ID_MODEL_FROM_DATABASE=TU117GL
 
@@ -35192,6 +35552,9 @@ pci:v000010DEd00002183*
 pci:v000010DEd00002184*
  ID_MODEL_FROM_DATABASE=TU116 [GeForce GTX 1660]
 
+pci:v000010DEd00002187*
+ ID_MODEL_FROM_DATABASE=TU116 [GeForce GTX 1650 SUPER]
+
 pci:v000010DEd00002191*
  ID_MODEL_FROM_DATABASE=TU116M [GeForce GTX 1660 Ti Mobile]
 
@@ -35201,6 +35564,9 @@ pci:v000010DEd000021AE*
 pci:v000010DEd000021BF*
  ID_MODEL_FROM_DATABASE=TU116GL
 
+pci:v000010DEd000021C4*
+ ID_MODEL_FROM_DATABASE=TU116 [GeForce GTX 1660 SUPER]
+
 pci:v000010DEd000021D1*
  ID_MODEL_FROM_DATABASE=TU116BM [GeForce GTX 1660 Ti Mobile]
 
@@ -35837,6 +36203,12 @@ pci:v000010ECd0000522A*
 pci:v000010ECd0000522Asv0000103Csd00008079*
  ID_MODEL_FROM_DATABASE=RTS522A PCI Express Card Reader (EliteBook 840 G3)
 
+pci:v000010ECd0000522Asv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=RTS522A PCI Express Card Reader (OMEN-17-w001nv)
+
+pci:v000010ECd0000522Asv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=RTS522A PCI Express Card Reader (ThinkPad E595)
+
 pci:v000010ECd00005249*
  ID_MODEL_FROM_DATABASE=RTS5249 PCI Express Card Reader
 
@@ -35870,6 +36242,9 @@ pci:v000010ECd00005286*
 pci:v000010ECd00005287*
  ID_MODEL_FROM_DATABASE=RTL8411B PCI Express Card Reader
 
+pci:v000010ECd00005287sv00001025sd00001094*
+ ID_MODEL_FROM_DATABASE=RTL8411B PCI Express Card Reader (Acer Aspire E5-575G)
+
 pci:v000010ECd00005288*
  ID_MODEL_FROM_DATABASE=RTS5288 PCI Express Card Reader
 
@@ -35967,7 +36342,7 @@ pci:v000010ECd00008139sv0000103Csd000030D9*
  ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (Presario C700)
 
 pci:v000010ECd00008139sv00001043sd00001045*
- ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (L8400B or L3C/S notebook)
+ ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (L8400B, L3C/S, X58LE notebook)
 
 pci:v000010ECd00008139sv00001043sd00008109*
  ID_MODEL_FROM_DATABASE=RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (P5P800-MX Mainboard)
@@ -36116,6 +36491,9 @@ pci:v000010ECd00008168*
 pci:v000010ECd00008168sv00001019sd00008168*
  ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (RTL8111/8168 PCI Express Gigabit Ethernet controller)
 
+pci:v000010ECd00008168sv00001025sd00001094*
+ ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Acer Aspire E5-575G)
+
 pci:v000010ECd00008168sv00001028sd00000283*
  ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Vostro 220)
 
@@ -36140,6 +36518,9 @@ pci:v000010ECd00008168sv0000103Csd00001950*
 pci:v000010ECd00008168sv0000103Csd00002A6F*
  ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Asus IPIBL-LB Motherboard)
 
+pci:v000010ECd00008168sv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (OMEN-17-w001nv)
+
 pci:v000010ECd00008168sv0000103Csd00008615*
  ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Pavilion Laptop 15-cw1xxx)
 
@@ -36191,9 +36572,18 @@ pci:v000010ECd00008168sv00001462sd00004180*
 pci:v000010ECd00008168sv00001462sd00007522*
  ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (X58 Pro-E)
 
+pci:v000010ECd00008168sv00001462sd00007C37*
+ ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (X570-A PRO motherboard)
+
 pci:v000010ECd00008168sv00001775sd000011CC*
  ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (CC11/CL11)
 
+pci:v000010ECd00008168sv000017AAsd00003814*
+ ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Z50-75)
+
+pci:v000010ECd00008168sv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (ThinkPad E595)
+
 pci:v000010ECd00008168sv00001849sd00008168*
  ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Motherboard (one of many))
 
@@ -36344,15 +36734,27 @@ pci:v000010ECd0000B723*
 pci:v000010ECd0000B723sv000010ECsd00008739*
  ID_MODEL_FROM_DATABASE=RTL8723BE PCIe Wireless Network Adapter (Dell Wireless 1801)
 
+pci:v000010ECd0000B723sv000017AAsd0000B736*
+ ID_MODEL_FROM_DATABASE=RTL8723BE PCIe Wireless Network Adapter (Z50-75)
+
 pci:v000010ECd0000B822*
  ID_MODEL_FROM_DATABASE=RTL8822BE 802.11a/b/g/n/ac WiFi adapter
 
 pci:v000010ECd0000B822sv0000103Csd0000831B*
  ID_MODEL_FROM_DATABASE=RTL8822BE 802.11a/b/g/n/ac WiFi adapter (Realtek RTL8822BE 802.11ac 2 × 2 Wi-Fi + Bluetooth 4.2 Combo Adapter (MU-MIMO supported))
 
+pci:v000010ECd0000B822sv000017AAsd00005124*
+ ID_MODEL_FROM_DATABASE=RTL8822BE 802.11a/b/g/n/ac WiFi adapter (ThinkPad E595)
+
+pci:v000010ECd0000B822sv000017AAsd0000B023*
+ ID_MODEL_FROM_DATABASE=RTL8822BE 802.11a/b/g/n/ac WiFi adapter (ThinkPad E595)
+
 pci:v000010ECd0000C821*
  ID_MODEL_FROM_DATABASE=RTL8821CE 802.11ac PCIe Wireless Network Adapter
 
+pci:v000010ECd0000C822*
+ ID_MODEL_FROM_DATABASE=RTL8822CE 802.11ac PCIe Wireless Network Adapter
+
 pci:v000010ECd0000D723*
  ID_MODEL_FROM_DATABASE=RTL8723DE 802.11b/g/n PCIe Adapter
 
@@ -36786,7 +37188,16 @@ pci:v00001102d00000005sv00001102sd00001003*
  ID_MODEL_FROM_DATABASE=EMU20k1 [Sound Blaster X-Fi Series] (X-Fi XtremeMusic)
 
 pci:v00001102d00000006*
- ID_MODEL_FROM_DATABASE=EMU10k1X [SB Live! Value/OEM Series]
+ ID_MODEL_FROM_DATABASE=EMU10k1X / CA0103 [SB Live! OEM / SB 5.1 / Ectiva 5.1]
+
+pci:v00001102d00000006sv00001102sd00001001*
+ ID_MODEL_FROM_DATABASE=EMU10k1X / CA0103 [SB Live! OEM / SB 5.1 / Ectiva 5.1] (SB0680 Sound Blaster 5.1)
+
+pci:v00001102d00000006sv00001102sd00001003*
+ ID_MODEL_FROM_DATABASE=EMU10k1X / CA0103 [SB Live! OEM / SB 5.1 / Ectiva 5.1] (SB0203 SB Live! 5.1 (Dell))
+
+pci:v00001102d00000006sv00001102sd00001004*
+ ID_MODEL_FROM_DATABASE=EMU10k1X / CA0103 [SB Live! OEM / SB 5.1 / Ectiva 5.1] (TP0033 Ectiva Audio 5.1)
 
 pci:v00001102d00000007*
  ID_MODEL_FROM_DATABASE=CA0106/CA0111 [SB Live!/Audigy/X-Fi Series]
@@ -41256,61 +41667,64 @@ pci:v00001148d00004300sv00001259sd00002977*
  ID_MODEL_FROM_DATABASE=SK-9872 Gigabit Ethernet Server Adapter (SK-NET GE-ZX dual link) (AT-2970TX/2TX Gigabit Ethernet Adapter)
 
 pci:v00001148d00004320*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001]
 
 pci:v00001148d00004320sv00001148sd00000121*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8001 Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (Marvell RDK-8001 Adapter)
 
 pci:v00001148d00004320sv00001148sd00000221*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8002 Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (Marvell RDK-8002 Adapter)
 
 pci:v00001148d00004320sv00001148sd00000321*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8003 Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (Marvell RDK-8003 Adapter)
 
 pci:v00001148d00004320sv00001148sd00000421*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8004 Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (Marvell RDK-8004 Adapter)
 
 pci:v00001148d00004320sv00001148sd00000621*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8006 Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (Marvell RDK-8006 Adapter)
 
 pci:v00001148d00004320sv00001148sd00000721*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8007 Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (Marvell RDK-8007 Adapter)
 
 pci:v00001148d00004320sv00001148sd00000821*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8008 Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (Marvell RDK-8008 Adapter)
 
 pci:v00001148d00004320sv00001148sd00000921*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8009 Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (Marvell RDK-8009 Adapter)
 
 pci:v00001148d00004320sv00001148sd00001121*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8011 Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (Marvell RDK-8011 Adapter)
 
 pci:v00001148d00004320sv00001148sd00001221*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (Marvell RDK-8012 Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (Marvell RDK-8012 Adapter)
 
 pci:v00001148d00004320sv00001148sd00003221*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9521 V2.0 10/100/1000Base-T Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (SK-9521 V2.0 10/100/1000Base-T Adapter)
 
 pci:v00001148d00004320sv00001148sd00005021*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9821 V2.0 Gigabit Ethernet 10/100/1000Base-T Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (SK-9821 V2.0 Gigabit Ethernet 10/100/1000Base-T Adapter)
 
 pci:v00001148d00004320sv00001148sd00005041*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9841 V2.0 Gigabit Ethernet 1000Base-LX Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (SK-9841 V2.0 Gigabit Ethernet 1000Base-LX Adapter)
 
 pci:v00001148d00004320sv00001148sd00005043*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9843 V2.0 Gigabit Ethernet 1000Base-SX Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (SK-9843 V2.0 Gigabit Ethernet 1000Base-SX Adapter)
 
 pci:v00001148d00004320sv00001148sd00005051*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9851 V2.0 Gigabit Ethernet 1000Base-SX Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (SK-9851 V2.0 Gigabit Ethernet 1000Base-SX Adapter)
 
 pci:v00001148d00004320sv00001148sd00005061*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9861 V2.0 Gigabit Ethernet 1000Base-SX Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (SK-9861 V2.0 Gigabit Ethernet 1000Base-SX Adapter)
 
 pci:v00001148d00004320sv00001148sd00005071*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter)
 
 pci:v00001148d00004320sv00001148sd00009521*
- ID_MODEL_FROM_DATABASE=SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC (SK-9521 10/100/1000Base-T Adapter)
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (SK-9521 10/100/1000Base-T Adapter)
+
+pci:v00001148d00004320sv00001259sd00002916*
+ ID_MODEL_FROM_DATABASE=SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001] (AT-2916T)
 
 pci:v00001148d00004400*
  ID_MODEL_FROM_DATABASE=SK-9Dxx Gigabit Ethernet Adapter
@@ -41319,7 +41733,46 @@ pci:v00001148d00004500*
  ID_MODEL_FROM_DATABASE=SK-9Mxx Gigabit Ethernet Adapter
 
 pci:v00001148d00009000*
- ID_MODEL_FROM_DATABASE=SK-9S21 10/100/1000Base-T Server Adapter, PCI-X, Copper RJ-45
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022]
+
+pci:v00001148d00009000sv00001148sd00002100*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (SK-9S21 10/100/1000Base-T Server Adapter, PCI-X, Copper RJ-45)
+
+pci:v00001148d00009000sv00001148sd00002200*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (SK-9S22 10/100/1000Base-T Dual Port Server Adapter, PCI-X, 2 Copper RJ-45)
+
+pci:v00001148d00009000sv00001148sd00002210*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (SK-9P22 10/100/1000 Base-T Dual Port PMC card)
+
+pci:v00001148d00009000sv00001148sd00002220*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (TPMC-GBE-CO)
+
+pci:v00001148d00009000sv00001148sd00008100*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (SK-9S81 1000Base-SX Server Adapter,PCI-X, Fiber SX/LC)
+
+pci:v00001148d00009000sv00001148sd00008200*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (SK-9S82 1000Base-SX Dual Port Server Adapter, PCI-X, 2 Fiber SX/LC)
+
+pci:v00001148d00009000sv00001148sd00008210*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (SK-9P82 1000 Base-SX Dual Port PMC card)
+
+pci:v00001148d00009000sv00001148sd00008220*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (TPMC-GBE-FI)
+
+pci:v00001148d00009000sv00001148sd00009100*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (SK-9S91 1000Base-LX Server Adapter,PCI-X, Fiber LX/LC)
+
+pci:v00001148d00009000sv00001148sd00009200*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (SK-9S92 1000Base-LX Dual Port Server Adapter, PCI-X, 2 Fiber LX/LC)
+
+pci:v00001148d00009000sv00001259sd00002973*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (AT-2971SX v2 Gigabit Adapter)
+
+pci:v00001148d00009000sv00001259sd00002974*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (AT-2971T v2 Gigabit Adapter)
+
+pci:v00001148d00009000sv00001259sd00002978*
+ ID_MODEL_FROM_DATABASE=SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022] (AT-2971LX Gigabit Adapter)
 
 pci:v00001148d00009843*
  ID_MODEL_FROM_DATABASE=[Fujitsu] Gigabit Ethernet
@@ -42416,6 +42869,9 @@ pci:v00001180d00000476sv0000103Csd000030C0*
 pci:v00001180d00000476sv0000103Csd000030C1*
  ID_MODEL_FROM_DATABASE=RL5c476 II (Compaq 6910p)
 
+pci:v00001180d00000476sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=RL5c476 II (X58LE)
+
 pci:v00001180d00000476sv00001043sd00001237*
  ID_MODEL_FROM_DATABASE=RL5c476 II (A6J-Q008)
 
@@ -42548,6 +43004,9 @@ pci:v00001180d00000592sv0000103Csd000030CC*
 pci:v00001180d00000592sv0000103Csd000030CF*
  ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter (Pavilion dv95xx/96xx/97xx/98xx series)
 
+pci:v00001180d00000592sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter (X58LE)
+
 pci:v00001180d00000592sv00001043sd00001237*
  ID_MODEL_FROM_DATABASE=R5C592 Memory Stick Bus Host Adapter (A6J-Q008)
 
@@ -42608,6 +43067,9 @@ pci:v00001180d00000822sv0000103Csd000030CC*
 pci:v00001180d00000822sv0000103Csd000030CF*
  ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (Pavilion dv9668eg Laptop)
 
+pci:v00001180d00000822sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (X58LE)
+
 pci:v00001180d00000822sv00001043sd00001237*
  ID_MODEL_FROM_DATABASE=R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter (A6J-Q008)
 
@@ -42701,6 +43163,9 @@ pci:v00001180d00000843sv0000103Csd000030B7*
 pci:v00001180d00000843sv0000103Csd000030CF*
  ID_MODEL_FROM_DATABASE=R5C843 MMC Host Controller (Pavilion dv9500/9600/9700 series)
 
+pci:v00001180d00000843sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=R5C843 MMC Host Controller (X58LE)
+
 pci:v00001180d00000843sv00001183sd00000843*
  ID_MODEL_FROM_DATABASE=R5C843 MMC Host Controller (Alienware Aurora m9700)
 
@@ -43712,6 +44177,12 @@ pci:v000011ABd00004380*
 pci:v000011ABd00004381*
  ID_MODEL_FROM_DATABASE=Yukon Optima 88E8059 [PCIe Gigabit Ethernet Controller with AVB]
 
+pci:v000011ABd00004381sv00001259sd00002803*
+ ID_MODEL_FROM_DATABASE=Yukon Optima 88E8059 [PCIe Gigabit Ethernet Controller with AVB] (AT-2814FX)
+
+pci:v000011ABd00004381sv00001259sd00002804*
+ ID_MODEL_FROM_DATABASE=Yukon Optima 88E8059 [PCIe Gigabit Ethernet Controller with AVB] (AT-2874xx)
+
 pci:v000011ABd00004611*
  ID_MODEL_FROM_DATABASE=GT-64115 System Controller
 
@@ -45531,7 +46002,7 @@ pci:v00001225*
  ID_VENDOR_FROM_DATABASE=Power I/O, Inc.
 
 pci:v00001227*
- ID_VENDOR_FROM_DATABASE=Tech-Source
+ ID_VENDOR_FROM_DATABASE=EIZO Rugged Solutions
 
 pci:v00001227d00000006*
  ID_MODEL_FROM_DATABASE=Raptor GFX 8P
@@ -53291,6 +53762,9 @@ pci:v0000144Dd0000A822sv00001028sd00001FF9*
 pci:v0000144Dd0000A822sv00001028sd00001FFA*
  ID_MODEL_FROM_DATABASE=NVMe SSD Controller 172Xa/172Xb (Express Flash PM1725b 12.8TB AIC)
 
+pci:v0000144Dd0000A824*
+ ID_MODEL_FROM_DATABASE=NVMe SSD Controller PM173X
+
 pci:v0000144E*
  ID_VENDOR_FROM_DATABASE=OLITEC
 
@@ -54614,6 +55088,9 @@ pci:v000014E4d0000165F*
 pci:v000014E4d0000165Fsv00001028sd000004F7*
  ID_MODEL_FROM_DATABASE=NetXtreme BCM5720 2-port Gigabit Ethernet PCIe (PowerEdge R320 server)
 
+pci:v000014E4d0000165Fsv00001028sd000008FD*
+ ID_MODEL_FROM_DATABASE=NetXtreme BCM5720 2-port Gigabit Ethernet PCIe (PowerEdge R6515/R7515 LOM)
+
 pci:v000014E4d0000165Fsv00001028sd000008FF*
  ID_MODEL_FROM_DATABASE=NetXtreme BCM5720 2-port Gigabit Ethernet PCIe (PowerEdge Rx5xx LOM Board)
 
@@ -56102,6 +56579,9 @@ pci:v000014E4d00004432*
 pci:v000014E4d00004464*
  ID_MODEL_FROM_DATABASE=BCM4364 802.11ac Wireless Network Adapter
 
+pci:v000014E4d00004488*
+ ID_MODEL_FROM_DATABASE=BCM4377b Wireless Network Adapter
+
 pci:v000014E4d00004610*
  ID_MODEL_FROM_DATABASE=BCM4610 Sentry5 PCI to SB Bridge
 
@@ -58355,6 +58835,12 @@ pci:v000015B3d00000264*
 pci:v000015B3d00000281*
  ID_MODEL_FROM_DATABASE=NPS-600 Flash Recovery
 
+pci:v000015B3d00000538*
+ ID_MODEL_FROM_DATABASE=MT2910 Family [ConnectX-7 Flash Recovery]
+
+pci:v000015B3d00000539*
+ ID_MODEL_FROM_DATABASE=MT2910 Family [ConnectX-7 Secure Flash Recovery]
+
 pci:v000015B3d00001002*
  ID_MODEL_FROM_DATABASE=MT25400 Family [ConnectX-2 Virtual Function]
 
@@ -58602,7 +59088,7 @@ pci:v000015B3d00001020*
  ID_MODEL_FROM_DATABASE=MT28860
 
 pci:v000015B3d00001021*
- ID_MODEL_FROM_DATABASE=MT28861
+ ID_MODEL_FROM_DATABASE=MT2910 Family [ConnectX-7]
 
 pci:v000015B3d00001974*
  ID_MODEL_FROM_DATABASE=MT28800 Family [ConnectX-5 PCIe Bridge]
@@ -58625,6 +59111,9 @@ pci:v000015B3d00004117*
 pci:v000015B3d00004117sv00001BD4sd00000039*
  ID_MODEL_FROM_DATABASE=MT27712A0-FDCF-AE (SN10XMP2P25)
 
+pci:v000015B3d00004117sv00001BD4sd0000003A*
+ ID_MODEL_FROM_DATABASE=MT27712A0-FDCF-AE (25G SFP28 SP EO251FM9 Adapter)
+
 pci:v000015B3d00004117sv00001BD4sd0000004D*
  ID_MODEL_FROM_DATABASE=MT27712A0-FDCF-AE (SN10XMP2P25,YZPC-01191-101)
 
@@ -60467,6 +60956,9 @@ pci:v0000168Cd00000041*
 pci:v0000168Cd00000042*
  ID_MODEL_FROM_DATABASE=QCA9377 802.11ac Wireless Network Adapter
 
+pci:v0000168Cd00000042sv000011ADsd000008A6*
+ ID_MODEL_FROM_DATABASE=QCA9377 802.11ac Wireless Network Adapter (Qualcomm Atheros QCA9377 802.11ac Wireless Network Adapter)
+
 pci:v0000168Cd00000046*
  ID_MODEL_FROM_DATABASE=QCA9984 802.11ac Wave 2 Wireless Network Adapter
 
@@ -61844,6 +62336,9 @@ pci:v000017D3d00001880sv000017D3sd00001883*
 pci:v000017D3d00001884*
  ID_MODEL_FROM_DATABASE=ARC-1884 series PCIe 3.0 to SAS/SATA 12/6Gb RAID Controller
 
+pci:v000017D3d0000188A*
+ ID_MODEL_FROM_DATABASE=ARC-1886 series PCIe 4.0 to NVMe/SAS/SATA 16/12/6Gb RAID Controller
+
 pci:v000017D5*
  ID_VENDOR_FROM_DATABASE=Exar Corp.
 
@@ -64041,7 +64536,7 @@ pci:v00001969d00001048*
  ID_MODEL_FROM_DATABASE=Attansic L1 Gigabit Ethernet
 
 pci:v00001969d00001048sv00001043sd00008226*
- ID_MODEL_FROM_DATABASE=Attansic L1 Gigabit Ethernet (P5KPL-VM Motherboard)
+ ID_MODEL_FROM_DATABASE=Attansic L1 Gigabit Ethernet (P5B-MX/WiFi-AP, P5KPL-VM Motherboard)
 
 pci:v00001969d00001062*
  ID_MODEL_FROM_DATABASE=AR8132 Fast Ethernet
@@ -64259,6 +64754,9 @@ pci:v00001987d00005007*
 pci:v00001987d00005012*
  ID_MODEL_FROM_DATABASE=E12 NVMe Controller
 
+pci:v00001987d00005016*
+ ID_MODEL_FROM_DATABASE=E16 PCIe4 NVMe Controller
+
 pci:v00001989*
  ID_VENDOR_FROM_DATABASE=Montilio Inc.
 
@@ -64490,27 +64988,72 @@ pci:v000019E5d00000123sv000019E5sd00003036*
 pci:v000019E5d00000200*
  ID_MODEL_FROM_DATABASE=Hi1822 Family (2*100GE)
 
+pci:v000019E5d00000200sv000019E5sd0000D139*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*100GE) (Hi1822 SP572 (2*100GE))
+
+pci:v000019E5d00000200sv000019E5sd0000D13D*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*100GE) (Hi1822 SC371 (2*100GE))
+
 pci:v000019E5d00000202*
  ID_MODEL_FROM_DATABASE=Hi1822 Family (2*32G FC)
 
+pci:v000019E5d00000202sv000019E5sd0000D302*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*32G FC) (Hi1822 SP521 (2*32G FC))
+
+pci:v000019E5d00000202sv000019E5sd0000D304*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*32G FC) (Hi1822 SP526 (2*32G FC))
+
 pci:v000019E5d00000203*
  ID_MODEL_FROM_DATABASE=Hi1822 Family (2*16G FC)
 
+pci:v000019E5d00000203sv000019E5sd0000D301*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*16G FC) (Hi1822 SP520 (2*16G FC))
+
+pci:v000019E5d00000203sv000019E5sd0000D305*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*16G FC) (Hi1822 SP525 (2*16G FC))
+
 pci:v000019E5d00000205*
  ID_MODEL_FROM_DATABASE=Hi1822 Family (2*100GE)
 
+pci:v000019E5d00000205sv000019E5sd0000DF27*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*100GE) (Hi1822 MZ731 MEZZ (2*100GE))
+
 pci:v000019E5d00000206*
  ID_MODEL_FROM_DATABASE=Hi1822 Family (2*25GE)
 
+pci:v000019E5d00000206sv000019E5sd0000D138*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*25GE) (Hi1822 SP582 (2*25GE))
+
+pci:v000019E5d00000206sv000019E5sd0000D13A*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*25GE) (Hi1822 SC381 (2*25GE))
+
 pci:v000019E5d00000210*
  ID_MODEL_FROM_DATABASE=Hi1822 Family (4*25GE)
 
+pci:v000019E5d00000210sv000019E5sd0000DF2E*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (4*25GE) (Hi1822 MZ532 MEZZ (4*25GE))
+
 pci:v000019E5d00000211*
  ID_MODEL_FROM_DATABASE=Hi1822 Family (4*25GE)
 
+pci:v000019E5d00000211sv000019E5sd0000D12F*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (4*25GE) (Hi1822 SP571 (4*25GE))
+
+pci:v000019E5d00000211sv000019E5sd0000D137*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (4*25GE) (Hi1822 SP581 (4*25GE))
+
+pci:v000019E5d00000211sv000019E5sd0000D142*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (4*25GE) (Hi1822 SP583 (4*25GE))
+
 pci:v000019E5d00000212*
  ID_MODEL_FROM_DATABASE=Hi1822 Family (2*8G FC)
 
+pci:v000019E5d00000212sv000019E5sd0000D303*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*8G FC) (Hi1822 SP522 (2*8G FC))
+
+pci:v000019E5d00000212sv000019E5sd0000D306*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (2*8G FC) (Hi1822 SP523 (2*8G FC))
+
 pci:v000019E5d00001710*
  ID_MODEL_FROM_DATABASE=iBMA Virtual Network Adapter
 
@@ -64520,6 +65063,15 @@ pci:v000019E5d00001711*
 pci:v000019E5d00001822*
  ID_MODEL_FROM_DATABASE=Hi1822 Family (4*25GE)
 
+pci:v000019E5d00001822sv000019E5sd0000D129*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (4*25GE) (Hi1822 SP570 (4*25GE))
+
+pci:v000019E5d00001822sv000019E5sd0000D136*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (4*25GE) (Hi1822 SP580 (4*25GE))
+
+pci:v000019E5d00001822sv000019E5sd0000D141*
+ ID_MODEL_FROM_DATABASE=Hi1822 Family (4*25GE) (Hi1822 SP583 (4*25GE))
+
 pci:v000019E5d0000371E*
  ID_MODEL_FROM_DATABASE=Hi1822 Family Virtual Bridge
 
@@ -64988,6 +65540,12 @@ pci:v00001AB8d00004006*
 pci:v00001AB9*
  ID_VENDOR_FROM_DATABASE=Espia Srl
 
+pci:v00001AC1*
+ ID_VENDOR_FROM_DATABASE=Global Unichip Corp.
+
+pci:v00001AC1d0000089A*
+ ID_MODEL_FROM_DATABASE=Coral Edge TPU
+
 pci:v00001AC8*
  ID_VENDOR_FROM_DATABASE=Aeroflex Gaisler
 
@@ -65075,6 +65633,12 @@ pci:v00001AEA*
 pci:v00001AEAd00006601*
  ID_MODEL_FROM_DATABASE=AU6601 PCI-E Flash card reader controller
 
+pci:v00001AEAd00006621*
+ ID_MODEL_FROM_DATABASE=AU6621 PCI-E Flash card reader controller
+
+pci:v00001AEAd00006625*
+ ID_MODEL_FROM_DATABASE=AU6625 PCI-E Flash card reader controller
+
 pci:v00001AEC*
  ID_VENDOR_FROM_DATABASE=Wolfson Microelectronics
 
@@ -65564,6 +66128,9 @@ pci:v00001B6Fd00007023*
 pci:v00001B6Fd00007052*
  ID_MODEL_FROM_DATABASE=EJ188/EJ198 USB 3.0 Host Controller
 
+pci:v00001B6Fd00007052sv00001849sd00007052*
+ ID_MODEL_FROM_DATABASE=EJ188/EJ198 USB 3.0 Host Controller (QC5000-ITX/PH)
+
 pci:v00001B73*
  ID_VENDOR_FROM_DATABASE=Fresco Logic
 
@@ -65925,7 +66492,7 @@ pci:v00001C2Cd000000A6*
  ID_MODEL_FROM_DATABASE=FBC1CG Capture 1x100Gb
 
 pci:v00001C2Cd000000A9*
- ID_MODEL_FROM_DATABASE=FBC2XGHH Capture 2x10Gb
+ ID_MODEL_FROM_DATABASE=FBC2XGHH Capture 2x10Gb [Latina]
 
 pci:v00001C2Cd000000AD*
  ID_MODEL_FROM_DATABASE=FBC2CGG3HL Capture 2x100Gb [Padua]
@@ -66041,15 +66608,39 @@ pci:v00001C5Cd00001284*
 pci:v00001C5Cd00001285*
  ID_MODEL_FROM_DATABASE=PC300 NVMe Solid State Drive 1TB
 
+pci:v00001C5Cd00001327*
+ ID_MODEL_FROM_DATABASE=BC501 NVMe Solid State Drive 512GB
+
 pci:v00001C5Cd00001504*
  ID_MODEL_FROM_DATABASE=SC300 512GB M.2 2280 SATA Solid State Drive
 
 pci:v00001C5F*
  ID_VENDOR_FROM_DATABASE=Beijing Memblaze Technology Co. Ltd.
 
+pci:v00001C5Fd0000000D*
+ ID_MODEL_FROM_DATABASE=PBlaze5 520/526 AIC
+
+pci:v00001C5Fd0000003D*
+ ID_MODEL_FROM_DATABASE=PBlaze5 920/926 AIC
+
+pci:v00001C5Fd0000010D*
+ ID_MODEL_FROM_DATABASE=PBlaze5 520/526 U.2
+
+pci:v00001C5Fd0000013D*
+ ID_MODEL_FROM_DATABASE=PBlaze5 920/926 U.2
+
 pci:v00001C5Fd00000540*
  ID_MODEL_FROM_DATABASE=PBlaze4 NVMe SSD
 
+pci:v00001C5Fd00000550*
+ ID_MODEL_FROM_DATABASE=PBlaze5 700/900
+
+pci:v00001C5Fd00000555*
+ ID_MODEL_FROM_DATABASE=PBlaze5 510/516
+
+pci:v00001C5Fd00000557*
+ ID_MODEL_FROM_DATABASE=PBlaze5 910/916
+
 pci:v00001C63*
  ID_VENDOR_FROM_DATABASE=Science and Research Centre of Computer Technology (JSC "NICEVT")
 
@@ -66782,6 +67373,12 @@ pci:v00001DBF*
 pci:v00001DBFd00000401*
  ID_MODEL_FROM_DATABASE=StarDragon4800 PCI Express Root Port
 
+pci:v00001DC5*
+ ID_VENDOR_FROM_DATABASE=FADU Inc.
+
+pci:v00001DCD*
+ ID_VENDOR_FROM_DATABASE=Liqid Inc.
+
 pci:v00001DD8*
  ID_VENDOR_FROM_DATABASE=Pensando Systems Inc
 
@@ -66953,6 +67550,9 @@ pci:v00001DF3d00000203sv00001DF3sd00000002*
 pci:v00001DF3d00000203sv00001DF3sd00000003*
  ID_MODEL_FROM_DATABASE=ACE-NIC100 Programmable Network Accelerator (ENA2100F)
 
+pci:v00001DF3d00000203sv00001DF3sd00000004*
+ ID_MODEL_FROM_DATABASE=ACE-NIC100 Programmable Network Accelerator (ENA2040F)
+
 pci:v00001DF3d00000204*
  ID_MODEL_FROM_DATABASE=ACE-NIC-NID Programmable Network Accelerator
 
@@ -67010,8 +67610,14 @@ pci:v00001E24d00001635*
 pci:v00001E26*
  ID_VENDOR_FROM_DATABASE=Fujitsu Client Computing Limited
 
+pci:v00001E36*
+ ID_VENDOR_FROM_DATABASE=Shanghai Enflame Technology Co. Ltd
+
+pci:v00001E36d00000001*
+ ID_MODEL_FROM_DATABASE=T10 [CloudBlazer]
+
 pci:v00001E38*
- ID_VENDOR_FROM_DATABASE=Thinci, Inc
+ ID_VENDOR_FROM_DATABASE=Blaize, Inc
 
 pci:v00001E3D*
  ID_VENDOR_FROM_DATABASE=Burlywood, Inc
@@ -67031,9 +67637,27 @@ pci:v00001E4Cd00000010sv00001E4Csd00000120*
 pci:v00001E57*
  ID_VENDOR_FROM_DATABASE=Beijing Panyi Technology Co., Ltd
 
+pci:v00001E57d00000100*
+ ID_MODEL_FROM_DATABASE=The device has already been deleted.
+
+pci:v00001E57d00000100sv00000000sd00000100*
+ ID_MODEL_FROM_DATABASE=The device has already been deleted. (PY8800 64GB Accelerator)
+
 pci:v00001E6B*
  ID_VENDOR_FROM_DATABASE=Axiado Corp.
 
+pci:v00001E89*
+ ID_VENDOR_FROM_DATABASE=ID Quantique SA
+
+pci:v00001E89d00000002*
+ ID_MODEL_FROM_DATABASE=Quantis-PCIe-40M
+
+pci:v00001E89d00000003*
+ ID_MODEL_FROM_DATABASE=Quantis-PCIe-240M
+
+pci:v00001E94*
+ ID_VENDOR_FROM_DATABASE=Calian SED
+
 pci:v00001FC0*
  ID_VENDOR_FROM_DATABASE=Ascom (Finland) Oy
 
@@ -69656,6 +70280,9 @@ pci:v00008086d00000154sv00001043sd00001477*
 pci:v00008086d00000154sv00001043sd00001517*
  ID_MODEL_FROM_DATABASE=3rd Gen Core processor DRAM Controller (Zenbook Prime UX31A)
 
+pci:v00008086d00000154sv000010CFsd000016BF*
+ ID_MODEL_FROM_DATABASE=3rd Gen Core processor DRAM Controller (LIFEBOOK E752)
+
 pci:v00008086d00000155*
  ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor PCI Express Root Port
 
@@ -69713,6 +70340,9 @@ pci:v00008086d00000166sv00001043sd00001517*
 pci:v00008086d00000166sv00001043sd00002103*
  ID_MODEL_FROM_DATABASE=3rd Gen Core processor Graphics Controller (N56VZ)
 
+pci:v00008086d00000166sv000010CFsd000016C1*
+ ID_MODEL_FROM_DATABASE=3rd Gen Core processor Graphics Controller (LIFEBOOK E752)
+
 pci:v00008086d0000016A*
  ID_MODEL_FROM_DATABASE=Xeon E3-1200 v2/3rd Gen Core processor Graphics Controller
 
@@ -72209,6 +72839,9 @@ pci:v00008086d0000101Esv00001179sd00000001*
 pci:v00008086d0000101Esv00008086sd0000101E*
  ID_MODEL_FROM_DATABASE=82540EP Gigabit Ethernet Controller (Mobile) (PRO/1000 MT Mobile Connection)
 
+pci:v00008086d0000101F*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller V710 for 5GBASE-T
+
 pci:v00008086d00001026*
  ID_MODEL_FROM_DATABASE=82545GM Gigabit Ethernet Controller
 
@@ -72413,6 +73046,9 @@ pci:v00008086d0000104F*
 pci:v00008086d00001050*
  ID_MODEL_FROM_DATABASE=82562EZ 10/100 Ethernet Controller
 
+pci:v00008086d00001050sv00001014sd00000287*
+ ID_MODEL_FROM_DATABASE=82562EZ 10/100 Ethernet Controller (ThinkCentre S50)
+
 pci:v00008086d00001050sv00001028sd0000019D*
  ID_MODEL_FROM_DATABASE=82562EZ 10/100 Ethernet Controller (Dimension 3000)
 
@@ -74087,6 +74723,9 @@ pci:v00008086d00001503*
 pci:v00008086d00001503sv00001043sd0000849C*
  ID_MODEL_FROM_DATABASE=82579V Gigabit Network Connection (P8P67 Deluxe Motherboard)
 
+pci:v00008086d00001503sv000010CFsd0000161C*
+ ID_MODEL_FROM_DATABASE=82579V Gigabit Network Connection (LIFEBOOK E752)
+
 pci:v00008086d00001507*
  ID_MODEL_FROM_DATABASE=Ethernet Express Module X520-P2
 
@@ -74429,6 +75068,9 @@ pci:v00008086d00001528sv00001137sd000000BF*
 pci:v00008086d00001528sv00001170sd00000052*
  ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2
 
+pci:v00008086d00001528sv000015D9sd00000734*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 (AOC-STG-I2T)
+
 pci:v00008086d00001528sv000017AAsd00001073*
  ID_MODEL_FROM_DATABASE=Ethernet Controller 10-Gigabit X540-AT2 (ThinkServer X540-T2 AnyFabric)
 
@@ -74678,6 +75320,9 @@ pci:v00008086d00001563sv0000193Dsd00001008*
 pci:v00008086d00001563sv0000193Dsd00001009*
  ID_MODEL_FROM_DATABASE=Ethernet Controller 10G X550T (560T-L)
 
+pci:v00008086d00001563sv0000193Dsd00001011*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller 10G X550T (UN-NIC-ETH563T-sL-2P)
+
 pci:v00008086d00001563sv00008086sd00000001*
  ID_MODEL_FROM_DATABASE=Ethernet Controller 10G X550T (Ethernet Converged Network Adapter X550-T2)
 
@@ -75071,6 +75716,30 @@ pci:v00008086d0000158Bsv00001137sd00000225*
 pci:v00008086d0000158Bsv00001137sd000002B4*
  ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Ethernet Network Adapter XXV710 OCP 2.0)
 
+pci:v00008086d0000158Bsv00001374sd00000230*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Single Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G1I71))
+
+pci:v00008086d0000158Bsv00001374sd00000231*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Single Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G1I71EU))
+
+pci:v00008086d0000158Bsv00001374sd00000234*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Dual Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G2I71))
+
+pci:v00008086d0000158Bsv00001374sd00000235*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Dual Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G2I71EU))
+
+pci:v00008086d0000158Bsv00001374sd00000238*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Quad Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G4I71L))
+
+pci:v00008086d0000158Bsv00001374sd00000239*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Quad Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G4I71LEU))
+
+pci:v00008086d0000158Bsv00001374sd0000023A*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Quad Port 25 Gigabit Ethernet PCI Express Server Adapter (PE31625G4I71L))
+
+pci:v00008086d0000158Bsv00001374sd0000023B*
+ ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Quad Port 25 Gigabit Ethernet PCI Express Server Adapter (PE31625G4I71LEU))
+
 pci:v00008086d0000158Bsv00001590sd00000000*
  ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Ethernet Network Adapter XXV710-2)
 
@@ -75383,9 +76052,27 @@ pci:v00008086d000015EF*
 pci:v00008086d000015F0*
  ID_MODEL_FROM_DATABASE=JHL7540 Thunderbolt 3 USB Controller [Titan Ridge DD 2018]
 
+pci:v00008086d000015F4*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (15) I219-LM
+
+pci:v00008086d000015F5*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (15) I219-V
+
 pci:v00008086d000015F6*
  ID_MODEL_FROM_DATABASE=I210 Gigabit Ethernet Connection
 
+pci:v00008086d000015F9*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (14) I219-LM
+
+pci:v00008086d000015FA*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (14) I219-V
+
+pci:v00008086d000015FB*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (13) I219-LM
+
+pci:v00008086d000015FC*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (13) I219-V
+
 pci:v00008086d000015FF*
  ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GBASE-T
 
@@ -75581,6 +76268,9 @@ pci:v00008086d00001903sv00001028sd000006DC*
 pci:v00008086d00001903sv00001028sd000006E4*
  ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Thermal Subsystem (XPS 15 9550)
 
+pci:v00008086d00001903sv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Thermal Subsystem (OMEN-17-w001nv)
+
 pci:v00008086d00001903sv000017AAsd0000225D*
  ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Thermal Subsystem (ThinkPad T480)
 
@@ -75626,6 +76316,9 @@ pci:v00008086d00001910*
 pci:v00008086d00001910sv00001028sd000006E4*
  ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers (XPS 15 9550)
 
+pci:v00008086d00001910sv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers (OMEN-17-w001nv)
+
 pci:v00008086d00001911*
  ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/v6 / E3-1500 v5 / 6th/7th/8th Gen Core Processor Gaussian Mixture Model
 
@@ -75665,6 +76358,9 @@ pci:v00008086d0000191B*
 pci:v00008086d0000191Bsv00001028sd000006E4*
  ID_MODEL_FROM_DATABASE=HD Graphics 530 (XPS 15 9550)
 
+pci:v00008086d0000191Bsv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=HD Graphics 530 (OMEN-17-w001nv)
+
 pci:v00008086d0000191D*
  ID_MODEL_FROM_DATABASE=HD Graphics P530
 
@@ -75926,6 +76622,18 @@ pci:v00008086d000019E0*
 pci:v00008086d000019E2*
  ID_MODEL_FROM_DATABASE=Atom Processor C3000 Series QuickAssist Technology
 
+pci:v00008086d00001A1C*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (17) I219-LM
+
+pci:v00008086d00001A1D*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (17) I219-V
+
+pci:v00008086d00001A1E*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (16) I219-LM
+
+pci:v00008086d00001A1F*
+ ID_MODEL_FROM_DATABASE=Ethernet Connection (16) I219-V
+
 pci:v00008086d00001A21*
  ID_MODEL_FROM_DATABASE=82840 840 [Carmel] Chipset Host Bridge (Hub A)
 
@@ -76673,6 +77381,9 @@ pci:v00008086d00001E03sv00001043sd00001477*
 pci:v00008086d00001E03sv00001043sd00001517*
  ID_MODEL_FROM_DATABASE=7 Series Chipset Family 6-port SATA Controller [AHCI mode] (Zenbook Prime UX31A)
 
+pci:v00008086d00001E03sv000010CFsd000016E2*
+ ID_MODEL_FROM_DATABASE=7 Series Chipset Family 6-port SATA Controller [AHCI mode] (LIFEBOOK E752)
+
 pci:v00008086d00001E03sv0000144Dsd0000C652*
  ID_MODEL_FROM_DATABASE=7 Series Chipset Family 6-port SATA Controller [AHCI mode] (NP300E5C series laptop)
 
@@ -76715,6 +77426,9 @@ pci:v00008086d00001E10sv00001043sd00001517*
 pci:v00008086d00001E10sv00001043sd000084CA*
  ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family PCI Express Root Port 1 (P8H77-I Motherboard)
 
+pci:v00008086d00001E10sv000010CFsd000016E9*
+ ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family PCI Express Root Port 1 (LIFEBOOK E752)
+
 pci:v00008086d00001E10sv0000144Dsd0000C652*
  ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family PCI Express Root Port 1 (NP300E5C series laptop)
 
@@ -76736,6 +77450,9 @@ pci:v00008086d00001E12sv00001043sd00001517*
 pci:v00008086d00001E14*
  ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 3
 
+pci:v00008086d00001E14sv000010CFsd000016E9*
+ ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 3 (LIFEBOOK E752)
+
 pci:v00008086d00001E16*
  ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family PCI Express Root Port 4
 
@@ -76772,6 +77489,9 @@ pci:v00008086d00001E1C*
 pci:v00008086d00001E1E*
  ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 8
 
+pci:v00008086d00001E1Esv000010CFsd000016E9*
+ ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 8 (LIFEBOOK E752)
+
 pci:v00008086d00001E1Esv00001849sd00001E1E*
  ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family PCI Express Root Port 8 (Motherboard)
 
@@ -76796,6 +77516,9 @@ pci:v00008086d00001E20sv00001043sd00008415*
 pci:v00008086d00001E20sv00001043sd00008445*
  ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family High Definition Audio Controller (P8Z77-V LX Motherboard)
 
+pci:v00008086d00001E20sv000010CFsd00001757*
+ ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family High Definition Audio Controller (LIFEBOOK E752)
+
 pci:v00008086d00001E20sv0000144Dsd0000C652*
  ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family High Definition Audio Controller (NP300E5C series laptop)
 
@@ -76817,6 +77540,9 @@ pci:v00008086d00001E22sv00001043sd00001517*
 pci:v00008086d00001E22sv00001043sd000084CA*
  ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family SMBus Controller (P8 series motherboard)
 
+pci:v00008086d00001E22sv000010CFsd000016E6*
+ ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family SMBus Controller (LIFEBOOK E752)
+
 pci:v00008086d00001E22sv0000144Dsd0000C652*
  ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family SMBus Controller (NP300E5C series laptop)
 
@@ -76847,6 +77573,9 @@ pci:v00008086d00001E26sv00001043sd00001517*
 pci:v00008086d00001E26sv00001043sd000084CA*
  ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family USB Enhanced Host Controller #1 (P8 series motherboard)
 
+pci:v00008086d00001E26sv000010CFsd000016E8*
+ ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family USB Enhanced Host Controller #1 (LIFEBOOK E752)
+
 pci:v00008086d00001E26sv0000144Dsd0000C652*
  ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family USB Enhanced Host Controller #1 (NP300E5C series laptop)
 
@@ -76868,6 +77597,9 @@ pci:v00008086d00001E2Dsv00001043sd00001517*
 pci:v00008086d00001E2Dsv00001043sd000084CA*
  ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family USB Enhanced Host Controller #2 (P8 series motherboard)
 
+pci:v00008086d00001E2Dsv000010CFsd000016E8*
+ ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family USB Enhanced Host Controller #2 (LIFEBOOK E752)
+
 pci:v00008086d00001E2Dsv0000144Dsd0000C652*
  ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family USB Enhanced Host Controller #2 (NP300E5C series laptop)
 
@@ -76895,6 +77627,9 @@ pci:v00008086d00001E31sv00001043sd00001517*
 pci:v00008086d00001E31sv00001043sd000084CA*
  ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB xHCI Host Controller (P8 series motherboard)
 
+pci:v00008086d00001E31sv000010CFsd000016EE*
+ ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB xHCI Host Controller (LIFEBOOK E752)
+
 pci:v00008086d00001E31sv000017AAsd000021F3*
  ID_MODEL_FROM_DATABASE=7 Series/C210 Series Chipset Family USB xHCI Host Controller (ThinkPad T430)
 
@@ -76919,6 +77654,9 @@ pci:v00008086d00001E3Asv00001043sd00001517*
 pci:v00008086d00001E3Asv00001043sd000084CA*
  ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family MEI Controller #1 (P8 series motherboard)
 
+pci:v00008086d00001E3Asv000010CFsd000016EA*
+ ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family MEI Controller #1 (LIFEBOOK E752)
+
 pci:v00008086d00001E3Asv0000144Dsd0000C652*
  ID_MODEL_FROM_DATABASE=7 Series/C216 Chipset Family MEI Controller #1 (NP300E5C series laptop)
 
@@ -77024,6 +77762,9 @@ pci:v00008086d00001E59sv00001043sd00001477*
 pci:v00008086d00001E59sv00001043sd00001517*
  ID_MODEL_FROM_DATABASE=HM76 Express Chipset LPC Controller (Zenbook Prime UX31A)
 
+pci:v00008086d00001E59sv000010CFsd000016E0*
+ ID_MODEL_FROM_DATABASE=HM76 Express Chipset LPC Controller (LIFEBOOK E752)
+
 pci:v00008086d00001E5A*
  ID_MODEL_FROM_DATABASE=7 Series Chipset Family LPC Controller
 
@@ -77900,6 +78641,9 @@ pci:v00008086d00002448sv0000103Csd000030A3*
 pci:v00008086d00002448sv0000103Csd000030C1*
  ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (Compaq 6910p)
 
+pci:v00008086d00002448sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (X58LE)
+
 pci:v00008086d00002448sv0000104Dsd0000902D*
  ID_MODEL_FROM_DATABASE=82801 Mobile PCI Bridge (VAIO VGN-NR120E)
 
@@ -79016,6 +79760,9 @@ pci:v00008086d000024D1sv00008086sd0000524C*
 pci:v00008086d000024D2*
  ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1
 
+pci:v00008086d000024D2sv00001014sd00000287*
+ ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (ThinkCentre S50)
+
 pci:v00008086d000024D2sv00001014sd000002DD*
  ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1 (eServer xSeries server mainboard)
 
@@ -79085,6 +79832,9 @@ pci:v00008086d000024D2sv00008086sd0000524C*
 pci:v00008086d000024D3*
  ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller
 
+pci:v00008086d000024D3sv00001014sd00000287*
+ ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (ThinkCentre S50)
+
 pci:v00008086d000024D3sv00001014sd000002DD*
  ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) SMBus Controller (eServer xSeries server mainboard)
 
@@ -79142,6 +79892,9 @@ pci:v00008086d000024D3sv00008086sd0000524C*
 pci:v00008086d000024D4*
  ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2
 
+pci:v00008086d000024D4sv00001014sd00000287*
+ ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (ThinkCentre S50)
+
 pci:v00008086d000024D4sv00001014sd000002DD*
  ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2 (eServer xSeries server mainboard)
 
@@ -79217,6 +79970,9 @@ pci:v00008086d000024D5*
 pci:v00008086d000024D5sv0000100Asd0000147B*
  ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (Abit IS7-E motherboard)
 
+pci:v00008086d000024D5sv00001014sd00000287*
+ ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (ThinkCentre S50)
+
 pci:v00008086d000024D5sv00001028sd00000168*
  ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller (Precision Workstation 670 Mainboard)
 
@@ -79268,6 +80024,9 @@ pci:v00008086d000024D6sv0000103Csd0000006A*
 pci:v00008086d000024D7*
  ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3
 
+pci:v00008086d000024D7sv00001014sd00000287*
+ ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (ThinkCentre S50)
+
 pci:v00008086d000024D7sv00001014sd000002ED*
  ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3 (xSeries server mainboard)
 
@@ -79331,6 +80090,9 @@ pci:v00008086d000024D7sv00008086sd0000524C*
 pci:v00008086d000024DB*
  ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller
 
+pci:v00008086d000024DBsv00001014sd00000287*
+ ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (ThinkCentre S50)
+
 pci:v00008086d000024DBsv00001014sd000002DD*
  ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) IDE Controller (eServer xSeries server mainboard)
 
@@ -79406,6 +80168,9 @@ pci:v00008086d000024DC*
 pci:v00008086d000024DD*
  ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller
 
+pci:v00008086d000024DDsv00001014sd00000287*
+ ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (ThinkCentre S50)
+
 pci:v00008086d000024DDsv00001014sd000002DD*
  ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller (eServer xSeries server mainboard)
 
@@ -79469,6 +80234,9 @@ pci:v00008086d000024DDsv00008086sd0000524C*
 pci:v00008086d000024DE*
  ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4
 
+pci:v00008086d000024DEsv00001014sd00000287*
+ ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4 (ThinkCentre S50)
+
 pci:v00008086d000024DEsv00001014sd000002ED*
  ID_MODEL_FROM_DATABASE=82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4 (xSeries server mainboard)
 
@@ -79781,6 +80549,9 @@ pci:v00008086d00002571*
 pci:v00008086d00002572*
  ID_MODEL_FROM_DATABASE=82865G Integrated Graphics Controller
 
+pci:v00008086d00002572sv00001014sd00000287*
+ ID_MODEL_FROM_DATABASE=82865G Integrated Graphics Controller (ThinkCentre S50)
+
 pci:v00008086d00002572sv00001028sd0000019D*
  ID_MODEL_FROM_DATABASE=82865G Integrated Graphics Controller (Dimension 3000)
 
@@ -81366,7 +82137,7 @@ pci:v00008086d000027B8sv0000103Csd00002A8C*
  ID_MODEL_FROM_DATABASE=82801GB/GR (ICH7 Family) LPC Interface Bridge (Compaq 500B Microtower)
 
 pci:v00008086d000027B8sv00001043sd00008179*
- ID_MODEL_FROM_DATABASE=82801GB/GR (ICH7 Family) LPC Interface Bridge (P5KPL-VM Motherboard)
+ ID_MODEL_FROM_DATABASE=82801GB/GR (ICH7 Family) LPC Interface Bridge (P5B-MX/WiFi-AP, P5KPL-VM Motherboard)
 
 pci:v00008086d000027B8sv0000107Bsd00005048*
  ID_MODEL_FROM_DATABASE=82801GB/GR (ICH7 Family) LPC Interface Bridge (E4500)
@@ -81450,7 +82221,7 @@ pci:v00008086d000027C0sv0000103Csd00002A8C*
  ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [IDE mode] (Compaq 500B Microtower)
 
 pci:v00008086d000027C0sv00001043sd00008179*
- ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [IDE mode] (P5KPL-VM Motherboard)
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [IDE mode] (P5B-MX/WiFi-AP, P5KPL-VM Motherboard)
 
 pci:v00008086d000027C0sv0000107Bsd00005048*
  ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SATA Controller [IDE mode] (E4500)
@@ -81585,7 +82356,7 @@ pci:v00008086d000027C8sv00001043sd00001237*
  ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (A6J-Q008)
 
 pci:v00008086d000027C8sv00001043sd00008179*
- ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (P5KPL-VM,P5LD2-VM Mainboard)
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (P5B-MX/WiFi-AP, P5KPL-VM, P5LD2-VM Mainboard)
 
 pci:v00008086d000027C8sv00001043sd000083AD*
  ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #1 (Eee PC 1015PX)
@@ -81660,7 +82431,7 @@ pci:v00008086d000027C9sv00001043sd00001237*
  ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (A6J-Q008)
 
 pci:v00008086d000027C9sv00001043sd00008179*
- ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (P5KPL-VM,P5LD2-VM Mainboard)
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (P5B-MX/WiFi-AP, P5KPL-VM, P5LD2-VM Mainboard)
 
 pci:v00008086d000027C9sv00001043sd000083AD*
  ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #2 (Eee PC 1015PX)
@@ -81735,7 +82506,7 @@ pci:v00008086d000027CAsv00001043sd00001237*
  ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (A6J-Q008)
 
 pci:v00008086d000027CAsv00001043sd00008179*
- ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (P5KPL-VM,P5LD2-VM Mainboard)
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (P5B-MX/WiFi-AP, P5KPL-VM, P5LD2-VM Mainboard)
 
 pci:v00008086d000027CAsv00001043sd000083AD*
  ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #3 (Eee PC 1015PX)
@@ -81804,7 +82575,7 @@ pci:v00008086d000027CBsv00001043sd00001237*
  ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (A6J-Q008)
 
 pci:v00008086d000027CBsv00001043sd00008179*
- ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (P5KPL-VM,P5LD2-VM Mainboard)
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (P5B-MX/WiFi-AP, P5KPL-VM, P5LD2-VM Mainboard)
 
 pci:v00008086d000027CBsv00001043sd000083AD*
  ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB UHCI Controller #4 (Eee PC 1015PX)
@@ -81879,7 +82650,7 @@ pci:v00008086d000027CCsv00001043sd00001237*
  ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (A6J-Q008)
 
 pci:v00008086d000027CCsv00001043sd00008179*
- ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (P5KPL-VM,P5LD2-VM Mainboard)
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (P5B-MX/WiFi-AP, P5KPL-VM, P5LD2-VM Mainboard)
 
 pci:v00008086d000027CCsv00001043sd000083AD*
  ID_MODEL_FROM_DATABASE=NM10/ICH7 Family USB2 EHCI Controller (Eee PC 1015PX)
@@ -82052,6 +82823,9 @@ pci:v00008086d000027D8sv00001043sd000013C4*
 pci:v00008086d000027D8sv00001043sd0000817F*
  ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (P5LD2-VM Mainboard (Realtek ALC 882 codec))
 
+pci:v00008086d000027D8sv00001043sd00008249*
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (P5B-MX/WiFi-AP)
+
 pci:v00008086d000027D8sv00001043sd00008290*
  ID_MODEL_FROM_DATABASE=NM10/ICH7 Family High Definition Audio Controller (P5KPL-VM Motherboard)
 
@@ -82143,7 +82917,7 @@ pci:v00008086d000027DAsv0000103Csd00002A8C*
  ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (Compaq 500B Microtower)
 
 pci:v00008086d000027DAsv00001043sd00008179*
- ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (P5KPL-VM Motherboard)
+ ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (P5B-MX/WiFi-AP, P5KPL-VM Motherboard)
 
 pci:v00008086d000027DAsv0000105Bsd00000D7C*
  ID_MODEL_FROM_DATABASE=NM10/ICH7 Family SMBus Controller (D270S/D250S Motherboard)
@@ -82236,7 +83010,7 @@ pci:v00008086d000027DFsv00001043sd00001237*
  ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (A6J-Q008)
 
 pci:v00008086d000027DFsv00001043sd00008179*
- ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (P5KPL-VM Motherboard)
+ ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (P5B-MX/WiFi-AP, P5KPL-VM Motherboard)
 
 pci:v00008086d000027DFsv0000107Bsd00005048*
  ID_MODEL_FROM_DATABASE=82801G (ICH7 Family) IDE Controller (E4500)
@@ -82316,6 +83090,9 @@ pci:v00008086d00002815sv0000103Csd000030CC*
 pci:v00008086d00002815sv0000103Csd000030D9*
  ID_MODEL_FROM_DATABASE=82801HM (ICH8M) LPC Interface Controller (Presario C700)
 
+pci:v00008086d00002815sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801HM (ICH8M) LPC Interface Controller (X58LE)
+
 pci:v00008086d00002815sv0000104Dsd00009005*
  ID_MODEL_FROM_DATABASE=82801HM (ICH8M) LPC Interface Controller (Vaio VGN-FZ260E)
 
@@ -82433,6 +83210,9 @@ pci:v00008086d00002829sv0000103Csd000030CC*
 pci:v00008086d00002829sv0000103Csd000030D9*
  ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] (Presario C700)
 
+pci:v00008086d00002829sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] (X58LE)
+
 pci:v00008086d00002829sv0000104Dsd00009005*
  ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] (Vaio VGN-FZ260E)
 
@@ -82484,6 +83264,9 @@ pci:v00008086d00002830sv0000103Csd000030CC*
 pci:v00008086d00002830sv0000103Csd000030D9*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 (Presario C700)
 
+pci:v00008086d00002830sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 (X58LE)
+
 pci:v00008086d00002830sv00001043sd000081EC*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #1 (P5B)
 
@@ -82532,6 +83315,9 @@ pci:v00008086d00002831sv0000103Csd000030CC*
 pci:v00008086d00002831sv0000103Csd000030D9*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 (Presario C700)
 
+pci:v00008086d00002831sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 (X58LE)
+
 pci:v00008086d00002831sv00001043sd000081EC*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #2 (P5B)
 
@@ -82580,6 +83366,9 @@ pci:v00008086d00002832sv0000103Csd000030CC*
 pci:v00008086d00002832sv0000103Csd000030D9*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #3 (Presario C700)
 
+pci:v00008086d00002832sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #3 (X58LE)
+
 pci:v00008086d00002832sv00001043sd000081EC*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #3 (P5B)
 
@@ -82628,6 +83417,9 @@ pci:v00008086d00002834sv0000103Csd000030C1*
 pci:v00008086d00002834sv0000103Csd000030CC*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 (Pavilion dv6700)
 
+pci:v00008086d00002834sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 (X58LE)
+
 pci:v00008086d00002834sv00001043sd000081EC*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #4 (P5B)
 
@@ -82673,6 +83465,9 @@ pci:v00008086d00002835sv0000103Csd000030C1*
 pci:v00008086d00002835sv0000103Csd000030CC*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #5 (Pavilion dv6700)
 
+pci:v00008086d00002835sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #5 (X58LE)
+
 pci:v00008086d00002835sv00001043sd000081EC*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB UHCI Controller #5 (P5B)
 
@@ -82718,6 +83513,9 @@ pci:v00008086d00002836sv0000103Csd000030CC*
 pci:v00008086d00002836sv0000103Csd000030D9*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 (Presario C700)
 
+pci:v00008086d00002836sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 (X58LE)
+
 pci:v00008086d00002836sv00001043sd000081EC*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #1 (P5B)
 
@@ -82763,6 +83561,9 @@ pci:v00008086d0000283Asv0000103Csd000030C1*
 pci:v00008086d0000283Asv0000103Csd000030CC*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #2 (Pavilion dv6700)
 
+pci:v00008086d0000283Asv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #2 (X58LE)
+
 pci:v00008086d0000283Asv00001043sd000081EC*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) USB2 EHCI Controller #2 (P5B)
 
@@ -82799,6 +83600,9 @@ pci:v00008086d0000283Esv00001028sd0000022F*
 pci:v00008086d0000283Esv0000103Csd000030D9*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) SMBus Controller (Presario C700)
 
+pci:v00008086d0000283Esv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) SMBus Controller (X58LE)
+
 pci:v00008086d0000283Esv00001043sd000081EC*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) SMBus Controller (P5B)
 
@@ -82832,6 +83636,9 @@ pci:v00008086d0000283Fsv00001028sd000001DA*
 pci:v00008086d0000283Fsv0000103Csd000030C1*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 1 (Compaq 6910p)
 
+pci:v00008086d0000283Fsv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 1 (X58LE)
+
 pci:v00008086d0000283Fsv0000104Dsd0000902D*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 1 (VAIO VGN-NR120E)
 
@@ -82847,6 +83654,9 @@ pci:v00008086d00002841*
 pci:v00008086d00002841sv0000103Csd000030C1*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 2 (Compaq 6910p)
 
+pci:v00008086d00002841sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 2 (X58LE)
+
 pci:v00008086d00002841sv0000104Dsd0000902D*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 2 (VAIO VGN-NR120E)
 
@@ -82859,6 +83669,9 @@ pci:v00008086d00002841sv000017C0sd00004083*
 pci:v00008086d00002843*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 3
 
+pci:v00008086d00002843sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 3 (X58LE)
+
 pci:v00008086d00002843sv0000104Dsd0000902D*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 3 (VAIO VGN-NR120E)
 
@@ -82871,6 +83684,9 @@ pci:v00008086d00002843sv000017C0sd00004083*
 pci:v00008086d00002845*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 4
 
+pci:v00008086d00002845sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 4 (X58LE)
+
 pci:v00008086d00002845sv000017AAsd000020AD*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) PCI Express Port 4 (ThinkPad T61/R61)
 
@@ -82940,6 +83756,9 @@ pci:v00008086d0000284Bsv0000103Csd000030CC*
 pci:v00008086d0000284Bsv00001043sd00001339*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (M51S series)
 
+pci:v00008086d0000284Bsv00001043sd000017F3*
+ ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (X58LE)
+
 pci:v00008086d0000284Bsv00001043sd000081EC*
  ID_MODEL_FROM_DATABASE=82801H (ICH8 Family) HD Audio Controller (P5B)
 
@@ -82997,6 +83816,9 @@ pci:v00008086d00002850sv0000103Csd000030CC*
 pci:v00008086d00002850sv0000103Csd000030D9*
  ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) IDE Controller (Presario C700)
 
+pci:v00008086d00002850sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) IDE Controller (X58LE)
+
 pci:v00008086d00002850sv0000104Dsd00009005*
  ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) IDE Controller (Vaio VGN-FZ260E)
 
@@ -83012,6 +83834,9 @@ pci:v00008086d00002850sv000017C0sd00004083*
 pci:v00008086d00002850sv0000E4BFsd0000CC47*
  ID_MODEL_FROM_DATABASE=82801HM/HEM (ICH8M/ICH8M-E) IDE Controller (CCG-RUMBA)
 
+pci:v00008086d000028C0*
+ ID_MODEL_FROM_DATABASE=Volume Management Device NVMe RAID Controller
+
 pci:v00008086d00002912*
  ID_MODEL_FROM_DATABASE=82801IH (ICH9DH) LPC Interface Controller
 
@@ -83753,12 +84578,18 @@ pci:v00008086d0000294Csv000017AAsd0000302E*
 pci:v00008086d00002970*
  ID_MODEL_FROM_DATABASE=82946GZ/PL/GL Memory Controller Hub
 
+pci:v00008086d00002970sv00001043sd0000823B*
+ ID_MODEL_FROM_DATABASE=82946GZ/PL/GL Memory Controller Hub (P5B-MX/WiFi-AP)
+
 pci:v00008086d00002971*
  ID_MODEL_FROM_DATABASE=82946GZ/PL/GL PCI Express Root Port
 
 pci:v00008086d00002972*
  ID_MODEL_FROM_DATABASE=82946GZ/GL Integrated Graphics Controller
 
+pci:v00008086d00002972sv00001043sd0000823B*
+ ID_MODEL_FROM_DATABASE=82946GZ/GL Integrated Graphics Controller (P5B-MX/WiFi-AP)
+
 pci:v00008086d00002973*
  ID_MODEL_FROM_DATABASE=82946GZ/GL Integrated Graphics Controller
 
@@ -84056,6 +84887,9 @@ pci:v00008086d00002A00sv0000103Csd000030CC*
 pci:v00008086d00002A00sv0000103Csd000030D9*
  ID_MODEL_FROM_DATABASE=Mobile PM965/GM965/GL960 Memory Controller Hub (Presario C700)
 
+pci:v00008086d00002A00sv00001043sd00001017*
+ ID_MODEL_FROM_DATABASE=Mobile PM965/GM965/GL960 Memory Controller Hub (X58LE)
+
 pci:v00008086d00002A00sv0000104Dsd00009005*
  ID_MODEL_FROM_DATABASE=Mobile PM965/GM965/GL960 Memory Controller Hub (Vaio VGN-FZ260E)
 
@@ -84095,6 +84929,9 @@ pci:v00008086d00002A02sv0000103Csd000030C0*
 pci:v00008086d00002A02sv0000103Csd000030D9*
  ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (primary) (Presario C700)
 
+pci:v00008086d00002A02sv00001043sd000014E2*
+ ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (primary) (X58LE)
+
 pci:v00008086d00002A02sv0000104Dsd0000902D*
  ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (primary) (VAIO VGN-NR120E)
 
@@ -84122,6 +84959,9 @@ pci:v00008086d00002A03sv0000103Csd000030C0*
 pci:v00008086d00002A03sv0000103Csd000030D9*
  ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (secondary) (Presario C700)
 
+pci:v00008086d00002A03sv00001043sd000014E2*
+ ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (secondary) (X58LE)
+
 pci:v00008086d00002A03sv0000104Dsd0000902D*
  ID_MODEL_FROM_DATABASE=Mobile GM965/GL960 Integrated Graphics Controller (secondary) (VAIO VGN-NR120E)
 
@@ -87245,6 +88085,9 @@ pci:v00008086d00003B42sv00001028sd0000040A*
 pci:v00008086d00003B42sv00001028sd0000040B*
  ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 1 (Latitude E6510)
 
+pci:v00008086d00003B42sv0000103Csd00001521*
+ ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 1 (EliteBook 8540p)
+
 pci:v00008086d00003B42sv0000144Dsd0000C06A*
  ID_MODEL_FROM_DATABASE=5 Series/3400 Series Chipset PCI Express Root Port 1 (R730 Laptop)
 
@@ -87617,6 +88460,9 @@ pci:v00008086d00003E1Fsv00001458sd00005000*
 pci:v00008086d00003E30*
  ID_MODEL_FROM_DATABASE=8th Gen Core 8-core Desktop Processor Host Bridge/DRAM Registers [Coffee Lake S]
 
+pci:v00008086d00003E33*
+ ID_MODEL_FROM_DATABASE=8th/9th Gen Core Processor Host Bridge/DRAM Registers [Coffee Lake]
+
 pci:v00008086d00003E34*
  ID_MODEL_FROM_DATABASE=Coffee Lake HOST and DRAM Controller
 
@@ -88043,6 +88889,12 @@ pci:v00008086d0000423Dsv00008086sd00001316*
 pci:v00008086d0000444E*
  ID_MODEL_FROM_DATABASE=Turbo Memory Controller
 
+pci:v00008086d0000467F*
+ ID_MODEL_FROM_DATABASE=Volume Management Device NVMe RAID Controller
+
+pci:v00008086d00004C3D*
+ ID_MODEL_FROM_DATABASE=Volume Management Device NVMe RAID Controller
+
 pci:v00008086d00005001*
  ID_MODEL_FROM_DATABASE=LE80578
 
@@ -88214,6 +89066,9 @@ pci:v00008086d00005902*
 pci:v00008086d00005904*
  ID_MODEL_FROM_DATABASE=Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers
 
+pci:v00008086d00005904sv00001025sd0000115F*
+ ID_MODEL_FROM_DATABASE=Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers (Aspire E5-575G)
+
 pci:v00008086d00005904sv000017AAsd00002247*
  ID_MODEL_FROM_DATABASE=Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers (ThinkPad T570)
 
@@ -88253,6 +89108,9 @@ pci:v00008086d00005914sv000017AAsd0000225D*
 pci:v00008086d00005916*
  ID_MODEL_FROM_DATABASE=HD Graphics 620
 
+pci:v00008086d00005916sv00001025sd00001094*
+ ID_MODEL_FROM_DATABASE=HD Graphics 620 (Aspire E5-575G)
+
 pci:v00008086d00005916sv000017AAsd00002248*
  ID_MODEL_FROM_DATABASE=HD Graphics 620 (ThinkPad T570)
 
@@ -90137,6 +90995,9 @@ pci:v00008086d00009641*
 pci:v00008086d000096A1*
  ID_MODEL_FROM_DATABASE=Integrated RAID
 
+pci:v00008086d00009A0B*
+ ID_MODEL_FROM_DATABASE=Volume Management Device NVMe RAID Controller
+
 pci:v00008086d00009B41*
  ID_MODEL_FROM_DATABASE=UHD Graphics
 
@@ -90458,6 +91319,9 @@ pci:v00008086d00009CE6*
 pci:v00008086d00009D03*
  ID_MODEL_FROM_DATABASE=Sunrise Point-LP SATA Controller [AHCI mode]
 
+pci:v00008086d00009D03sv00001025sd0000115F*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP SATA Controller [AHCI mode] (Acer Aspire E5-575G)
+
 pci:v00008086d00009D03sv00001028sd000006DC*
  ID_MODEL_FROM_DATABASE=Sunrise Point-LP SATA Controller [AHCI mode] (Latitude E7470)
 
@@ -90518,6 +91382,9 @@ pci:v00008086d00009D1A*
 pci:v00008086d00009D21*
  ID_MODEL_FROM_DATABASE=Sunrise Point-LP PMC
 
+pci:v00008086d00009D21sv00001025sd0000115F*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP PMC (Acer Aspire E5-575G)
+
 pci:v00008086d00009D21sv00001028sd000006DC*
  ID_MODEL_FROM_DATABASE=Sunrise Point-LP PMC (Latitude E7470)
 
@@ -90539,6 +91406,9 @@ pci:v00008086d00009D21sv000017AAsd0000382A*
 pci:v00008086d00009D23*
  ID_MODEL_FROM_DATABASE=Sunrise Point-LP SMBus
 
+pci:v00008086d00009D23sv00001025sd0000115F*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP SMBus (Acer Aspire E5-575G)
+
 pci:v00008086d00009D23sv00001028sd000006DC*
  ID_MODEL_FROM_DATABASE=Sunrise Point-LP SMBus (Latitude E7470)
 
@@ -90578,6 +91448,9 @@ pci:v00008086d00009D2D*
 pci:v00008086d00009D2F*
  ID_MODEL_FROM_DATABASE=Sunrise Point-LP USB 3.0 xHCI Controller
 
+pci:v00008086d00009D2Fsv00001025sd0000115F*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP USB 3.0 xHCI Controller (Acer Aspire E5-575G)
+
 pci:v00008086d00009D2Fsv00001028sd000006DC*
  ID_MODEL_FROM_DATABASE=Sunrise Point-LP USB 3.0 xHCI Controller (Latitude E7470)
 
@@ -90599,6 +91472,9 @@ pci:v00008086d00009D2Fsv000017AAsd0000382A*
 pci:v00008086d00009D31*
  ID_MODEL_FROM_DATABASE=Sunrise Point-LP Thermal subsystem
 
+pci:v00008086d00009D31sv00001025sd0000115F*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP Thermal subsystem (Acer Aspire E5-575G)
+
 pci:v00008086d00009D31sv00001028sd000006DC*
  ID_MODEL_FROM_DATABASE=Sunrise Point-LP Thermal subsystem (Latitude E7470)
 
@@ -90626,6 +91502,9 @@ pci:v00008086d00009D35*
 pci:v00008086d00009D3A*
  ID_MODEL_FROM_DATABASE=Sunrise Point-LP CSME HECI #1
 
+pci:v00008086d00009D3Asv00001025sd0000115F*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP CSME HECI #1 (Acer Aspire E5-575G)
+
 pci:v00008086d00009D3Asv00001028sd000006DC*
  ID_MODEL_FROM_DATABASE=Sunrise Point-LP CSME HECI #1 (Latitude E7470)
 
@@ -90686,6 +91565,9 @@ pci:v00008086d00009D56*
 pci:v00008086d00009D58*
  ID_MODEL_FROM_DATABASE=Sunrise Point-LP LPC Controller
 
+pci:v00008086d00009D58sv00001025sd0000115F*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP LPC Controller (Acer Aspire E5-575G)
+
 pci:v00008086d00009D58sv000017AAsd00002247*
  ID_MODEL_FROM_DATABASE=Sunrise Point-LP LPC Controller (ThinkPad T570)
 
@@ -90695,6 +91577,9 @@ pci:v00008086d00009D58sv000017AAsd0000224F*
 pci:v00008086d00009D60*
  ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO I2C Controller #0
 
+pci:v00008086d00009D60sv00001025sd0000115F*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO I2C Controller #0 (Acer Aspire E5-575G)
+
 pci:v00008086d00009D60sv00001028sd000006F3*
  ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO I2C Controller #0 (Latitude 3570)
 
@@ -90743,6 +91628,9 @@ pci:v00008086d00009D70sv000017AAsd0000382A*
 pci:v00008086d00009D71*
  ID_MODEL_FROM_DATABASE=Sunrise Point-LP HD Audio
 
+pci:v00008086d00009D71sv00001025sd00001094*
+ ID_MODEL_FROM_DATABASE=Sunrise Point-LP HD Audio (Acer Aspire E5-575G)
+
 pci:v00008086d00009D71sv000017AAsd0000224F*
  ID_MODEL_FROM_DATABASE=Sunrise Point-LP HD Audio (ThinkPad X1 Carbon 5th Gen)
 
@@ -90767,6 +91655,9 @@ pci:v00008086d00009DB0*
 pci:v00008086d00009DB1*
  ID_MODEL_FROM_DATABASE=Cannon Point-LP PCI Express Root Port #10
 
+pci:v00008086d00009DB2*
+ ID_MODEL_FROM_DATABASE=Cannon Point-LP PCI Express Root Port #1
+
 pci:v00008086d00009DB4*
  ID_MODEL_FROM_DATABASE=Cannon Point-LP PCI Express Root Port #13
 
@@ -90803,6 +91694,9 @@ pci:v00008086d00009DD3*
 pci:v00008086d00009DE0*
  ID_MODEL_FROM_DATABASE=Cannon Point-LP MEI Controller #1
 
+pci:v00008086d00009DE3*
+ ID_MODEL_FROM_DATABASE=Cannon Point-LP Keyboard and Text (KT) Redirection
+
 pci:v00008086d00009DE8*
  ID_MODEL_FROM_DATABASE=Cannon Point-LP Serial IO I2C Controller #0
 
@@ -90902,6 +91796,9 @@ pci:v00008086d0000A103*
 pci:v00008086d0000A103sv00001028sd000006E4*
  ID_MODEL_FROM_DATABASE=HM170/QM170 Chipset SATA Controller [AHCI Mode] (XPS 15 9550)
 
+pci:v00008086d0000A103sv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=HM170/QM170 Chipset SATA Controller [AHCI Mode] (OMEN-17-w001nv)
+
 pci:v00008086d0000A105*
  ID_MODEL_FROM_DATABASE=Sunrise Point-H SATA Controller [RAID mode]
 
@@ -90971,6 +91868,9 @@ pci:v00008086d0000A121*
 pci:v00008086d0000A121sv00001028sd000006E4*
  ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Power Management Controller (XPS 15 9550)
 
+pci:v00008086d0000A121sv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Power Management Controller (OMEN-17-w001nv)
+
 pci:v00008086d0000A122*
  ID_MODEL_FROM_DATABASE=Sunrise Point-H cAVS
 
@@ -90980,6 +91880,9 @@ pci:v00008086d0000A123*
 pci:v00008086d0000A123sv00001028sd000006E4*
  ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family SMBus (XPS 15 9550)
 
+pci:v00008086d0000A123sv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family SMBus (OMEN-17-w001nv)
+
 pci:v00008086d0000A124*
  ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family SPI Controller
 
@@ -91007,6 +91910,9 @@ pci:v00008086d0000A12F*
 pci:v00008086d0000A12Fsv00001028sd000006E4*
  ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family USB 3.0 xHCI Controller (XPS 15 9550)
 
+pci:v00008086d0000A12Fsv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family USB 3.0 xHCI Controller (OMEN-17-w001nv)
+
 pci:v00008086d0000A130*
  ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family USB Device Controller (OTG)
 
@@ -91016,6 +91922,9 @@ pci:v00008086d0000A131*
 pci:v00008086d0000A131sv00001028sd000006E4*
  ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Thermal Subsystem (XPS 15 9550)
 
+pci:v00008086d0000A131sv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Thermal Subsystem (OMEN-17-w001nv)
+
 pci:v00008086d0000A133*
  ID_MODEL_FROM_DATABASE=Sunrise Point-H Northpeak ACPI Function
 
@@ -91028,6 +91937,9 @@ pci:v00008086d0000A13A*
 pci:v00008086d0000A13Asv00001028sd000006E4*
  ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family MEI Controller #1 (XPS 15 9550)
 
+pci:v00008086d0000A13Asv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family MEI Controller #1 (OMEN-17-w001nv)
+
 pci:v00008086d0000A13B*
  ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family MEI Controller #2
 
@@ -91088,6 +92000,9 @@ pci:v00008086d0000A14E*
 pci:v00008086d0000A14Esv00001028sd000006E4*
  ID_MODEL_FROM_DATABASE=HM170 Chipset LPC/eSPI Controller (XPS 15 9550)
 
+pci:v00008086d0000A14Esv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=HM170 Chipset LPC/eSPI Controller (OMEN-17-w001nv)
+
 pci:v00008086d0000A14F*
  ID_MODEL_FROM_DATABASE=Sunrise Point-H LPC Controller
 
@@ -91145,6 +92060,9 @@ pci:v00008086d0000A160*
 pci:v00008086d0000A160sv00001028sd000006E4*
  ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Serial IO I2C Controller #0 (XPS 15 9550)
 
+pci:v00008086d0000A160sv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Serial IO I2C Controller #0 (OMEN-17-w001nv)
+
 pci:v00008086d0000A161*
  ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family Serial IO I2C Controller #1
 
@@ -91178,6 +92096,9 @@ pci:v00008086d0000A170*
 pci:v00008086d0000A170sv00001028sd000006E4*
  ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family HD Audio Controller (XPS 15 9550)
 
+pci:v00008086d0000A170sv0000103Csd0000825B*
+ ID_MODEL_FROM_DATABASE=100 Series/C230 Series Chipset Family HD Audio Controller (OMEN-17-w001nv)
+
 pci:v00008086d0000A171*
  ID_MODEL_FROM_DATABASE=CM238 HD Audio Controller
 
@@ -91568,6 +92489,9 @@ pci:v00008086d0000A323*
 pci:v00008086d0000A324*
  ID_MODEL_FROM_DATABASE=Cannon Lake PCH SPI Controller
 
+pci:v00008086d0000A328*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH Serial IO UART Host Controller
+
 pci:v00008086d0000A32C*
  ID_MODEL_FROM_DATABASE=Cannon Lake PCH PCI Express Root Port #21
 
@@ -91655,6 +92579,9 @@ pci:v00008086d0000A360*
 pci:v00008086d0000A363*
  ID_MODEL_FROM_DATABASE=Cannon Lake PCH Active Management Technology - SOL
 
+pci:v00008086d0000A364*
+ ID_MODEL_FROM_DATABASE=Cannon Lake PCH HECI Controller #2
+
 pci:v00008086d0000A368*
  ID_MODEL_FROM_DATABASE=Cannon Lake PCH Serial IO I2C Controller #0
 
@@ -91796,12 +92723,66 @@ pci:v00008086d0000F1A5*
 pci:v00008086d0000F1A6*
  ID_MODEL_FROM_DATABASE=SSD Pro 7600p/760p/E 6100p Series
 
+pci:v00008086d0000F1A6sv00008086sd0000390B*
+ ID_MODEL_FROM_DATABASE=SSD Pro 7600p/760p/E 6100p Series (Intel Corporation SSD Pro 7600p/760p/E 6100p Series [NVM Express])
+
 pci:v00008086d0000F1A8*
  ID_MODEL_FROM_DATABASE=SSD 660P Series
 
 pci:v00008088*
  ID_VENDOR_FROM_DATABASE=Beijing Wangxun Technology Co., Ltd.
 
+pci:v00008088d00000101*
+ ID_MODEL_FROM_DATABASE=WX1860A2 Gigabit Ethernet Controller
+
+pci:v00008088d00000101sv00008088sd00000201*
+ ID_MODEL_FROM_DATABASE=WX1860A2 Gigabit Ethernet Controller (Dual-Port Ethernet Network Adaptor SF200T)
+
+pci:v00008088d00000102*
+ ID_MODEL_FROM_DATABASE=WX1860A2S Gigabit Ethernet Controller
+
+pci:v00008088d00000102sv00008088sd00000210*
+ ID_MODEL_FROM_DATABASE=WX1860A2S Gigabit Ethernet Controller (Dual-Port Ethernet Network Adaptor SF200T-S)
+
+pci:v00008088d00000103*
+ ID_MODEL_FROM_DATABASE=WX1860A4 Gigabit Ethernet Controller
+
+pci:v00008088d00000103sv00008088sd00000401*
+ ID_MODEL_FROM_DATABASE=WX1860A4 Gigabit Ethernet Controller (Qual-Port Ethernet Network Adaptor SF400T)
+
+pci:v00008088d00000103sv00008088sd00000440*
+ ID_MODEL_FROM_DATABASE=WX1860A4 Gigabit Ethernet Controller (Qual-Port Ethernet Network Adaptor SF400-OCP)
+
+pci:v00008088d00000104*
+ ID_MODEL_FROM_DATABASE=WX1860A4S Gigabit Ethernet Controller
+
+pci:v00008088d00000104sv00008088sd00000410*
+ ID_MODEL_FROM_DATABASE=WX1860A4S Gigabit Ethernet Controller (Qual-Port Ethernet Network Adaptor SF400T-S)
+
+pci:v00008088d00000105*
+ ID_MODEL_FROM_DATABASE=WX1860AL2 Gigabit Ethernet Controller
+
+pci:v00008088d00000105sv00008088sd00000202*
+ ID_MODEL_FROM_DATABASE=WX1860AL2 Gigabit Ethernet Controller (Dual-Port Ethernet Network Adaptor SF200HT)
+
+pci:v00008088d00000106*
+ ID_MODEL_FROM_DATABASE=WX1860AL2S Gigabit Ethernet Controller
+
+pci:v00008088d00000106sv00008088sd00000220*
+ ID_MODEL_FROM_DATABASE=WX1860AL2S Gigabit Ethernet Controller (Dual-Port Ethernet Network Adaptor SF200HT-S)
+
+pci:v00008088d00000107*
+ ID_MODEL_FROM_DATABASE=WX1860AL4 Gigabit Ethernet Controller
+
+pci:v00008088d00000107sv00008088sd00000402*
+ ID_MODEL_FROM_DATABASE=WX1860AL4 Gigabit Ethernet Controller (Qual-Port Ethernet Network Adaptor SF400HT)
+
+pci:v00008088d00000108*
+ ID_MODEL_FROM_DATABASE=WX1860AL4S Gigabit Ethernet Controller
+
+pci:v00008088d00000108sv00008088sd00000420*
+ ID_MODEL_FROM_DATABASE=WX1860AL4S Gigabit Ethernet Controller (Qual-Port Ethernet Network Adaptor SF400HT-S)
+
 pci:v00008088d00001001*
  ID_MODEL_FROM_DATABASE=Ethernet Controller RP1000 for 10GbE SFP+
 
@@ -93746,6 +94727,18 @@ pci:v0000D161d00008013*
 pci:v0000D161d0000B410*
  ID_MODEL_FROM_DATABASE=Wildcard B410 quad-BRI card
 
+pci:v0000D209*
+ ID_VENDOR_FROM_DATABASE=Ultimarc
+
+pci:v0000D209d00001500*
+ ID_MODEL_FROM_DATABASE=PAC Drive
+
+pci:v0000D209d000015A2*
+ ID_MODEL_FROM_DATABASE=SpinTrak
+
+pci:v0000D209d00001601*
+ ID_MODEL_FROM_DATABASE=AimTrak
+
 pci:v0000D4D4*
  ID_VENDOR_FROM_DATABASE=Dy4 Systems Inc
 
@@ -94253,30 +95246,102 @@ pci:v0000F1D0d0000CAFE*
 pci:v0000F1D0d0000CFEE*
  ID_MODEL_FROM_DATABASE=Xena LS/SD-22-DA/SD-DA
 
+pci:v0000F1D0d0000DAFE*
+ ID_MODEL_FROM_DATABASE=Corvid 1
+
 pci:v0000F1D0d0000DAFF*
  ID_MODEL_FROM_DATABASE=KONA LHi
 
+pci:v0000F1D0d0000DB00*
+ ID_MODEL_FROM_DATABASE=IoExpress
+
 pci:v0000F1D0d0000DB01*
  ID_MODEL_FROM_DATABASE=Corvid22
 
+pci:v0000F1D0d0000DB02*
+ ID_MODEL_FROM_DATABASE=Kona 3G
+
+pci:v0000F1D0d0000DB03*
+ ID_MODEL_FROM_DATABASE=Corvid 3G
+
+pci:v0000F1D0d0000DB04*
+ ID_MODEL_FROM_DATABASE=Kona 3G QUAD
+
+pci:v0000F1D0d0000DB05*
+ ID_MODEL_FROM_DATABASE=Kona LHe+
+
+pci:v0000F1D0d0000DB06*
+ ID_MODEL_FROM_DATABASE=IoXT
+
+pci:v0000F1D0d0000DB07*
+ ID_MODEL_FROM_DATABASE=Kona 3G P2P
+
+pci:v0000F1D0d0000DB08*
+ ID_MODEL_FROM_DATABASE=Kona 3G QUAD P2P
+
 pci:v0000F1D0d0000DB09*
  ID_MODEL_FROM_DATABASE=Corvid 24
 
+pci:v0000F1D0d0000DB11*
+ ID_MODEL_FROM_DATABASE=T-Tap
+
 pci:v0000F1D0d0000DCAF*
  ID_MODEL_FROM_DATABASE=Kona HD
 
 pci:v0000F1D0d0000DFEE*
  ID_MODEL_FROM_DATABASE=Xena HD-DA
 
+pci:v0000F1D0d0000EB07*
+ ID_MODEL_FROM_DATABASE=Io4K
+
+pci:v0000F1D0d0000EB0A*
+ ID_MODEL_FROM_DATABASE=Io4K UFC
+
+pci:v0000F1D0d0000EB0B*
+ ID_MODEL_FROM_DATABASE=Kona 4
+
+pci:v0000F1D0d0000EB0C*
+ ID_MODEL_FROM_DATABASE=Kona 4 UFC
+
 pci:v0000F1D0d0000EB0D*
  ID_MODEL_FROM_DATABASE=Corvid 88
 
 pci:v0000F1D0d0000EB0E*
  ID_MODEL_FROM_DATABASE=Corvid 44
 
+pci:v0000F1D0d0000EB16*
+ ID_MODEL_FROM_DATABASE=Corvid HEVC
+
+pci:v0000F1D0d0000EB16sv000010CFsd00001049*
+ ID_MODEL_FROM_DATABASE=Corvid HEVC (M31)
+
+pci:v0000F1D0d0000EB18*
+ ID_MODEL_FROM_DATABASE=Corvid HB-R
+
+pci:v0000F1D0d0000EB1A*
+ ID_MODEL_FROM_DATABASE=Kona IP 1SFP
+
+pci:v0000F1D0d0000EB1C*
+ ID_MODEL_FROM_DATABASE=Kona IP 2SFP
+
 pci:v0000F1D0d0000EB1D*
+ ID_MODEL_FROM_DATABASE=Io4KPlus
+
+pci:v0000F1D0d0000EB1E*
+ ID_MODEL_FROM_DATABASE=IoIP
+
+pci:v0000F1D0d0000EB1F*
  ID_MODEL_FROM_DATABASE=Kona 5
 
+pci:v0000F1D0d0000EB23*
+ ID_MODEL_FROM_DATABASE=Kona 1
+
+pci:v0000F1D0d0000EB24*
+ ID_MODEL_FROM_DATABASE=Kona HDMI
+
+pci:v0000F1D0d0000EB25*
+ ID_MODEL_FROM_DATABASE=Corvid 44 12g
+
 pci:v0000F1D0d0000EFAC*
  ID_MODEL_FROM_DATABASE=Xena SD-MM/SD-22-MM
 
index 0de5135b24849183c02f676d234c0e8343347125..38de451b51f27ad3f00ef16dc8389577dd5f9b87 100644 (file)
@@ -68,6 +68,9 @@ usb:v0085*
 usb:v0085p0600*
  ID_MODEL_FROM_DATABASE=eBook Reader
 
+usb:v0102*
+ ID_VENDOR_FROM_DATABASE=miniSTREAK
+
 usb:v0105*
  ID_VENDOR_FROM_DATABASE=Trust International B.V.
 
@@ -458,6 +461,9 @@ usb:v03EBp7800*
 usb:v03EBp800C*
  ID_MODEL_FROM_DATABASE=Airspy HF+
 
+usb:v03EBpFF01*
+ ID_MODEL_FROM_DATABASE=WootingOne
+
 usb:v03EBpFF02*
  ID_MODEL_FROM_DATABASE=WootingTwo
 
@@ -2048,6 +2054,9 @@ usb:v03F0pC111*
 usb:v03F0pC202*
  ID_MODEL_FROM_DATABASE=PhotoSmart 8200 series
 
+usb:v03F0pC211*
+ ID_MODEL_FROM_DATABASE=Deskjet 2540 series
+
 usb:v03F0pC302*
  ID_MODEL_FROM_DATABASE=DeskJet D2300
 
@@ -2336,6 +2345,9 @@ usb:v0403p8371*
 usb:v0403p8372*
  ID_MODEL_FROM_DATABASE=FT8U100AX Serial Port
 
+usb:v0403p8508*
+ ID_MODEL_FROM_DATABASE=Selectronic SP PRO
+
 usb:v0403p87D0*
  ID_MODEL_FROM_DATABASE=Cressi Dive Computer Interface
 
@@ -2378,6 +2390,9 @@ usb:v0403p9136*
 usb:v0403p9E90*
  ID_MODEL_FROM_DATABASE=Marvell OpenRD Base/Client
 
+usb:v0403p9F08*
+ ID_MODEL_FROM_DATABASE=CIB-1894 Conclusion SmartLink Box:
+
 usb:v0403p9F80*
  ID_MODEL_FROM_DATABASE=Ewert Energy Systems CANdapter
 
@@ -3437,6 +3452,9 @@ usb:v040B*
 usb:v040Bp0A68*
  ID_MODEL_FROM_DATABASE=Func MS-3 gaming mouse [WT6573F MCU]
 
+usb:v040Bp2000*
+ ID_MODEL_FROM_DATABASE=wired Keyboard [Dynex DX-WRK1401]
+
 usb:v040Bp2367*
  ID_MODEL_FROM_DATABASE=Human Interface Device [HP CalcPad 200 Calculator and Numeric Keypad]
 
@@ -5882,6 +5900,9 @@ usb:v0450*
 usb:v0451*
  ID_VENDOR_FROM_DATABASE=Texas Instruments, Inc.
 
+usb:v0451p0422*
+ ID_MODEL_FROM_DATABASE=TUSB422 Port Controller with Power Delivery
+
 usb:v0451p1234*
  ID_MODEL_FROM_DATABASE=Bluetooth Device
 
@@ -5891,9 +5912,18 @@ usb:v0451p1428*
 usb:v0451p1446*
  ID_MODEL_FROM_DATABASE=TUSB2040/2070 Hub
 
+usb:v0451p16A2*
+ ID_MODEL_FROM_DATABASE=CC Debugger
+
 usb:v0451p16A6*
  ID_MODEL_FROM_DATABASE=BM-USBD1 BlueRobin RF heart rate sensor receiver
 
+usb:v0451p16A8*
+ ID_MODEL_FROM_DATABASE=CC2531 ZigBee
+
+usb:v0451p16AE*
+ ID_MODEL_FROM_DATABASE=CC2531 Dongle
+
 usb:v0451p2036*
  ID_MODEL_FROM_DATABASE=TUSB2036 Hub
 
@@ -5996,6 +6026,9 @@ usb:v0451pE013*
 usb:v0451pE01C*
  ID_MODEL_FROM_DATABASE=Data Collection Sled [Nspire Lab Cradle, Nspire Datatracker Cradle]
 
+usb:v0451pE01E*
+ ID_MODEL_FROM_DATABASE=Nspire\99 CX Navigator\99 Access Point
+
 usb:v0451pE01F*
  ID_MODEL_FROM_DATABASE=Python Adapter (firmware install mode)
 
@@ -6092,6 +6125,9 @@ usb:v0458p0003*
 usb:v0458p0006*
  ID_MODEL_FROM_DATABASE=Easy Mouse+
 
+usb:v0458p0007*
+ ID_MODEL_FROM_DATABASE=Trackbar Emotion
+
 usb:v0458p000B*
  ID_MODEL_FROM_DATABASE=NetMouse Wheel(P+U)
 
@@ -6710,6 +6746,9 @@ usb:v045Ep00CE*
 usb:v045Ep00D1*
  ID_MODEL_FROM_DATABASE=Optical Mouse with Tilt Wheel
 
+usb:v045Ep00D2*
+ ID_MODEL_FROM_DATABASE=Notebook Optical Mouse with Tilt Wheel
+
 usb:v045Ep00DA*
  ID_MODEL_FROM_DATABASE=eHome Infrared Receiver
 
@@ -7364,6 +7403,9 @@ usb:v045Ep07F8*
 usb:v045Ep07FD*
  ID_MODEL_FROM_DATABASE=Nano Transceiver 1.1
 
+usb:v045Ep0810*
+ ID_MODEL_FROM_DATABASE=LifeCam HD-3000
+
 usb:v045Ep0900*
  ID_MODEL_FROM_DATABASE=Surface Dock Hub
 
@@ -7595,6 +7637,9 @@ usb:v0461p4D75*
 usb:v0461p4D81*
  ID_MODEL_FROM_DATABASE=Dell N889 Optical Mouse
 
+usb:v0461p4D8A*
+ ID_MODEL_FROM_DATABASE=HP Multimedia Keyboard
+
 usb:v0461p4D91*
  ID_MODEL_FROM_DATABASE=Laser mouse M-D16DL
 
@@ -7613,6 +7658,9 @@ usb:v0461p4DE7*
 usb:v0461p4E04*
  ID_MODEL_FROM_DATABASE=Lenovo Keyboard KB1021
 
+usb:v0461p4E6F*
+ ID_MODEL_FROM_DATABASE=Acer Wired Keyboard Model KBAY211
+
 usb:v0463*
  ID_VENDOR_FROM_DATABASE=MGE UPS Systems
 
@@ -7697,6 +7745,9 @@ usb:v046Ap0106*
 usb:v046Ap010D*
  ID_MODEL_FROM_DATABASE=MX-Board 3.0 Keyboard
 
+usb:v046Ap0180*
+ ID_MODEL_FROM_DATABASE=Strait 3.0
+
 usb:v046ApB090*
  ID_MODEL_FROM_DATABASE=Keyboard
 
@@ -8168,6 +8219,9 @@ usb:v046Dp0A4D*
 usb:v046Dp0A5B*
  ID_MODEL_FROM_DATABASE=G933 Wireless Headset Dongle
 
+usb:v046Dp0A5D*
+ ID_MODEL_FROM_DATABASE=G933 Headset Battery Charger
+
 usb:v046Dp0A66*
  ID_MODEL_FROM_DATABASE=[G533 Wireless Headset Dongle]
 
@@ -8555,6 +8609,9 @@ usb:v046DpC245*
 usb:v046DpC246*
  ID_MODEL_FROM_DATABASE=Gaming Mouse G300
 
+usb:v046DpC247*
+ ID_MODEL_FROM_DATABASE=G100S Optical Gaming Mouse
+
 usb:v046DpC248*
  ID_MODEL_FROM_DATABASE=G105 Gaming Keyboard
 
@@ -8699,6 +8756,9 @@ usb:v046DpC326*
 usb:v046DpC328*
  ID_MODEL_FROM_DATABASE=Corded Keyboard K280e
 
+usb:v046DpC32B*
+ ID_MODEL_FROM_DATABASE=G910 Orion Spark Mechanical Keyboard
+
 usb:v046DpC332*
  ID_MODEL_FROM_DATABASE=G502 Proteus Spectrum Optical Mouse
 
@@ -8822,6 +8882,12 @@ usb:v046DpC532*
 usb:v046DpC534*
  ID_MODEL_FROM_DATABASE=Unifying Receiver
 
+usb:v046DpC537*
+ ID_MODEL_FROM_DATABASE=Cordless Mouse Receiver
+
+usb:v046DpC53A*
+ ID_MODEL_FROM_DATABASE=PowerPlay Wireless Charging System
+
 usb:v046DpC603*
  ID_MODEL_FROM_DATABASE=3Dconnexion Spacemouse Plus XT
 
@@ -9683,6 +9749,9 @@ usb:v047F*
 usb:v047Fp0101*
  ID_MODEL_FROM_DATABASE=Bulk Driver
 
+usb:v047Fp02EE*
+ ID_MODEL_FROM_DATABASE=BT600
+
 usb:v047Fp0301*
  ID_MODEL_FROM_DATABASE=Bulk Driver
 
@@ -9710,6 +9779,9 @@ usb:v047FpC008*
 usb:v047FpC00E*
  ID_MODEL_FROM_DATABASE=Blackwire C310 headset
 
+usb:v047FpC03B*
+ ID_MODEL_FROM_DATABASE=HD1
+
 usb:v0480*
  ID_VENDOR_FROM_DATABASE=Toshiba America Inc
 
@@ -9737,6 +9809,9 @@ usb:v0480p0820*
 usb:v0480p0821*
  ID_MODEL_FROM_DATABASE=Canvio Advance 2TB model DTC920
 
+usb:v0480p0900*
+ ID_MODEL_FROM_DATABASE=MQ04UBF100
+
 usb:v0480pA006*
  ID_MODEL_FROM_DATABASE=External Disk 1.5TB
 
@@ -9944,6 +10019,9 @@ usb:v0483p91D1*
 usb:v0483pA171*
  ID_MODEL_FROM_DATABASE=ThermaData WiFi
 
+usb:v0483pA2E0*
+ ID_MODEL_FROM_DATABASE=BMeasure instrument
+
 usb:v0483pDF11*
  ID_MODEL_FROM_DATABASE=STM Device in DFU Mode
 
@@ -10031,6 +10109,9 @@ usb:v048Dp1165*
 usb:v048Dp1172*
  ID_MODEL_FROM_DATABASE=Flash Drive
 
+usb:v048Dp1234*
+ ID_MODEL_FROM_DATABASE=Mass storage
+
 usb:v048Dp1336*
  ID_MODEL_FROM_DATABASE=SD/MMC Cardreader
 
@@ -11066,6 +11147,9 @@ usb:v04A9p10CA*
 usb:v04A9p10E3*
  ID_MODEL_FROM_DATABASE=PIXMA iX6850 Printer
 
+usb:v04A9p12FE*
+ ID_MODEL_FROM_DATABASE=Printer in service mode
+
 usb:v04A9p1404*
  ID_MODEL_FROM_DATABASE=W6400PG
 
@@ -11411,6 +11495,9 @@ usb:v04A9p178D*
 usb:v04A9p180B*
  ID_MODEL_FROM_DATABASE=PIXMA MG3000 series
 
+usb:v04A9p1856*
+ ID_MODEL_FROM_DATABASE=PIXMA TS6250
+
 usb:v04A9p1900*
  ID_MODEL_FROM_DATABASE=CanoScan LiDE 90
 
@@ -11615,6 +11702,9 @@ usb:v04A9p2634*
 usb:v04A9p2635*
  ID_MODEL_FROM_DATABASE=MPC190
 
+usb:v04A9p2636*
+ ID_MODEL_FROM_DATABASE=LBP3200
+
 usb:v04A9p2637*
  ID_MODEL_FROM_DATABASE=iR C6800
 
@@ -11657,12 +11747,18 @@ usb:v04A9p2650*
 usb:v04A9p2651*
  ID_MODEL_FROM_DATABASE=iR 3100C EUR
 
+usb:v04A9p2654*
+ ID_MODEL_FROM_DATABASE=LBP3600
+
 usb:v04A9p2655*
  ID_MODEL_FROM_DATABASE=FP-L170/MF350/L380/L398
 
 usb:v04A9p2656*
  ID_MODEL_FROM_DATABASE=iR1510-1670 CAPT Printer
 
+usb:v04A9p2657*
+ ID_MODEL_FROM_DATABASE=LBP3210
+
 usb:v04A9p2659*
  ID_MODEL_FROM_DATABASE=MF8100
 
@@ -11703,7 +11799,7 @@ usb:v04A9p2669*
  ID_MODEL_FROM_DATABASE=iR105PLUS
 
 usb:v04A9p266A*
- ID_MODEL_FROM_DATABASE=CAPT Device
+ ID_MODEL_FROM_DATABASE=LBP3000
 
 usb:v04A9p266B*
  ID_MODEL_FROM_DATABASE=iR8070
@@ -11748,7 +11844,7 @@ usb:v04A9p2678*
  ID_MODEL_FROM_DATABASE=iR 2570C EUR
 
 usb:v04A9p2679*
- ID_MODEL_FROM_DATABASE=CAPT Device
+ ID_MODEL_FROM_DATABASE=LBP5000
 
 usb:v04A9p267A*
  ID_MODEL_FROM_DATABASE=iR2016
@@ -11759,6 +11855,9 @@ usb:v04A9p267B*
 usb:v04A9p267D*
  ID_MODEL_FROM_DATABASE=MF7100 series
 
+usb:v04A9p267E*
+ ID_MODEL_FROM_DATABASE=LBP3300
+
 usb:v04A9p2684*
  ID_MODEL_FROM_DATABASE=MF3200 series
 
@@ -11777,6 +11876,9 @@ usb:v04A9p2689*
 usb:v04A9p268A*
  ID_MODEL_FROM_DATABASE=LC310/L390/L408S
 
+usb:v04A9p268B*
+ ID_MODEL_FROM_DATABASE=LBP3500
+
 usb:v04A9p268C*
  ID_MODEL_FROM_DATABASE=iR C6870
 
@@ -11792,9 +11894,15 @@ usb:v04A9p268F*
 usb:v04A9p2691*
  ID_MODEL_FROM_DATABASE=iR7105
 
+usb:v04A9p26A1*
+ ID_MODEL_FROM_DATABASE=LBP5300
+
 usb:v04A9p26A3*
  ID_MODEL_FROM_DATABASE=MF4100 series
 
+usb:v04A9p26A4*
+ ID_MODEL_FROM_DATABASE=LBP5100
+
 usb:v04A9p26B0*
  ID_MODEL_FROM_DATABASE=MF4600 series
 
@@ -11807,21 +11915,54 @@ usb:v04A9p26B5*
 usb:v04A9p26B6*
  ID_MODEL_FROM_DATABASE=FAX-L140/L130
 
+usb:v04A9p26B9*
+ ID_MODEL_FROM_DATABASE=LBP3310
+
+usb:v04A9p26BA*
+ ID_MODEL_FROM_DATABASE=LBP5050
+
 usb:v04A9p26DA*
- ID_MODEL_FROM_DATABASE=LBP3010B printer
+ ID_MODEL_FROM_DATABASE=LBP3010/LBP3018/LBP3050
+
+usb:v04A9p26DB*
+ ID_MODEL_FROM_DATABASE=LBP3100/LBP3108/LBP3150
 
 usb:v04A9p26E6*
  ID_MODEL_FROM_DATABASE=iR1024
 
+usb:v04A9p26EA*
+ ID_MODEL_FROM_DATABASE=LBP9100C
+
+usb:v04A9p26EE*
+ ID_MODEL_FROM_DATABASE=MF4320-4350
+
+usb:v04A9p26F1*
+ ID_MODEL_FROM_DATABASE=LBP7200C
+
+usb:v04A9p26FF*
+ ID_MODEL_FROM_DATABASE=LBP6300
+
 usb:v04A9p271A*
  ID_MODEL_FROM_DATABASE=LBP6000
 
+usb:v04A9p271B*
+ ID_MODEL_FROM_DATABASE=LBP6200
+
+usb:v04A9p271C*
+ ID_MODEL_FROM_DATABASE=LBP7010C/7018C
+
 usb:v04A9p2736*
  ID_MODEL_FROM_DATABASE=I-SENSYS MF4550d
 
 usb:v04A9p2737*
  ID_MODEL_FROM_DATABASE=MF4410
 
+usb:v04A9p2771*
+ ID_MODEL_FROM_DATABASE=LBP6020
+
+usb:v04A9p2796*
+ ID_MODEL_FROM_DATABASE=LBP6230/6240
+
 usb:v04A9p3041*
  ID_MODEL_FROM_DATABASE=PowerShot S10
 
@@ -13028,6 +13169,9 @@ usb:v04B0p042A*
 usb:v04B0p0430*
  ID_MODEL_FROM_DATABASE=D7100
 
+usb:v04B0p0436*
+ ID_MODEL_FROM_DATABASE=D810
+
 usb:v04B0p043F*
  ID_MODEL_FROM_DATABASE=D5600
 
@@ -13211,6 +13355,9 @@ usb:v04B4p4616*
 usb:v04B4p4624*
  ID_MODEL_FROM_DATABASE=DS-Xtreme Flash Card
 
+usb:v04B4p4717*
+ ID_MODEL_FROM_DATABASE=West Bridge
+
 usb:v04B4p5201*
  ID_MODEL_FROM_DATABASE=Combi Keyboard-Hub (Hub)
 
@@ -35001,7 +35148,7 @@ usb:v09C2*
  ID_VENDOR_FROM_DATABASE=Nisca Corp.
 
 usb:v09C3*
- ID_VENDOR_FROM_DATABASE=ActivCard, Inc.
+ ID_VENDOR_FROM_DATABASE=HID Global
 
 usb:v09C3p0007*
  ID_MODEL_FROM_DATABASE=Reader V2
@@ -35012,6 +35159,24 @@ usb:v09C3p0008*
 usb:v09C3p0014*
  ID_MODEL_FROM_DATABASE=ActivIdentity ActivKey SIM USB Token
 
+usb:v09C3p0028*
+ ID_MODEL_FROM_DATABASE=Crescendo Key
+
+usb:v09C3p0029*
+ ID_MODEL_FROM_DATABASE=Crescendo Key
+
+usb:v09C3p002A*
+ ID_MODEL_FROM_DATABASE=Crescendo Key
+
+usb:v09C3p002B*
+ ID_MODEL_FROM_DATABASE=Crescendo Key
+
+usb:v09C3p002C*
+ ID_MODEL_FROM_DATABASE=Crescendo Key
+
+usb:v09C3p002E*
+ ID_MODEL_FROM_DATABASE=Crescendo Key
+
 usb:v09C4*
  ID_VENDOR_FROM_DATABASE=ACTiSYS Corp.
 
@@ -57233,6 +57398,30 @@ usb:v2237*
 usb:v2237p4161*
  ID_MODEL_FROM_DATABASE=eReader White
 
+usb:v224F*
+ ID_VENDOR_FROM_DATABASE=APDM
+
+usb:v224Fp0001*
+ ID_MODEL_FROM_DATABASE=Access Point
+
+usb:v224Fp0002*
+ ID_MODEL_FROM_DATABASE=Docking Station
+
+usb:v224Fp0004*
+ ID_MODEL_FROM_DATABASE=V2 Opal ACM
+
+usb:v224Fp0005*
+ ID_MODEL_FROM_DATABASE=V2 Opal
+
+usb:v224Fp0006*
+ ID_MODEL_FROM_DATABASE=V2 Docking Station
+
+usb:v224Fp0007*
+ ID_MODEL_FROM_DATABASE=V2 Access Point ACM
+
+usb:v224Fp0008*
+ ID_MODEL_FROM_DATABASE=V2 Access Point
+
 usb:v225D*
  ID_VENDOR_FROM_DATABASE=Morpho
 
@@ -57863,6 +58052,12 @@ usb:v2548p1001*
 usb:v2548p1002*
  ID_MODEL_FROM_DATABASE=CEC Adapter
 
+usb:v25B5*
+ ID_VENDOR_FROM_DATABASE=FlatFrog
+
+usb:v25B5p0002*
+ ID_MODEL_FROM_DATABASE=Multitouch 3200
+
 usb:v2632*
  ID_VENDOR_FROM_DATABASE=TwinMOS
 
index 1474a860b34cd3411ee4e899917faad1bd7f7a2c..fae0ecc0facda0728ca0c7a50fbbde0bb3fa7ed2 100644 (file)
@@ -1200,6 +1200,19 @@ evdev:name:MSI Laptop hotkeys:dmi:bvn*:bvr*:bd*:svn*:pnM[iI][cC][rR][oO]-S[tT][a
  KEYBOARD_KEY_0213=f22
  KEYBOARD_KEY_0214=f23
 
+###########################################################
+# Olimex
+###########################################################
+
+# Teres-I
+evdev:input:b0003v15BAp003C*
+ KEYBOARD_KEY_70066=sleep                               # Fn+F1
+ KEYBOARD_KEY_700f6=wlan                                # Fn+F2
+ KEYBOARD_KEY_700c7=f21                                 # Fn+F3 touchpad toggle
+ KEYBOARD_KEY_7006f=brightnessdown                      # Fn+F7
+ KEYBOARD_KEY_70070=brightnessup                        # Fn+F8
+ KEYBOARD_KEY_7006e=switchvideomode                     # Fn+F9
+
 ###########################################################
 # OLPC
 ###########################################################
@@ -1558,6 +1571,14 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnVIA:pnK8N800:pvr*
 evdev:name:SIPODEV USB Composite Device:dmi:bvn*:bvr*:bd*:svnVIOS:pnLTH17:pvr*
  KEYBOARD_KEY_70073=f21                                 # Touchpad toggle
 
+###########################################################
+# WeiHeng
+###########################################################
+
+# P325J
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnINET:pnP325J:pvr*
+ KEYBOARD_KEY_76=f21                                    # Touchpad toggle
+
 ###########################################################
 # Zepto
 ###########################################################
index be9c8d2c4bdd22b3c4630060ddf64ed43eba4222..abedbaca812945e4642817f60143acc6004e9b8f 100644 (file)
 #
 # Allowed properties are:
 #    ACCEL_MOUNT_MATRIX=<matrix>
+#    PROXIMITY_NEAR_LEVEL=<value>
 #
 # where <matrix> is a mount-matrix in the format specified in the IIO
 # subsystem[1]. The default, when unset, is equivalent to:
 #   ACCEL_MOUNT_MATRIX=1, 0, 0; 0, 1, 0; 0, 0, 1
 # eg. the identity matrix.
+# and <value> is an integer value above which an object is considered
+# close by a proximity sensor:
+#   PROXIMITY_NEAR_LEVEL=100
 #
 # [1]: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=dfc57732ad38f93ae6232a3b4e64fd077383a0f1
 #
@@ -82,7 +86,7 @@ sensor:modalias:acpi:SMO8500:*:dmi:*Acer*:pnOneS1002*
 
 sensor:modalias:acpi:KIOX0009*:dmi:*:svnAcer:pnOneS1003:*
  ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+
 sensor:modalias:acpi:BOSC0200*:dmi:*:svnAcer*:pnSwitchSW312-31:*
  ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
 
@@ -105,20 +109,14 @@ sensor:modalias:acpi:INVN6500*:dmi:*svnASUSTeK*:pnT300CHI*
  ACCEL_MOUNT_MATRIX=0, -1, 0; 1, 0, 0; 0, 0, 1
 
 sensor:modalias:acpi:INVN6500*:dmi:*svnASUSTeK*:*pnT100TA*
- ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
-
 sensor:modalias:acpi:INVN6500*:dmi:*svnASUSTeK*:pnT200TA*
  ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
 
 sensor:modalias:acpi:INVN6500*:dmi:*svnASUSTeK*:*pnTP201SA*
- ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
-
 sensor:modalias:acpi:INVN6500*:dmi:*svnASUSTeK*:pn*E205SA*
  ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
 
 sensor:modalias:acpi:INVN6500*:dmi:*svn*ASUSTeK*:*pn*TP300LA*
- ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
-
 sensor:modalias:acpi:INVN6500*:dmi:*svn*ASUSTeK*:*pn*TP300LD*
  ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
 
@@ -131,6 +129,7 @@ sensor:modalias:acpi:KXJ2109*:dmi:*:svnASUSTeK*:pnME176C*
 sensor:modalias:acpi:SMO8500*:dmi:*svn*ASUSTeK*:*pn*TP300LJ*
  ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
 
+sensor:modalias:acpi:SMO8500*:dmi:*svn*ASUSTeK*:*pn*TP500LAB*
 sensor:modalias:acpi:SMO8500*:dmi:*svn*ASUSTeK*:*pn*TP500LB*
  ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
 
@@ -171,7 +170,7 @@ sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:*:svnDefaul
  ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
 
 # Chuwi Hi10 (CWI1515)
-sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvrP02A_C106.60E:*:svnDefaultstring:pnDefaultstring:* 
+sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvrP02A_C106.60E:*:svnDefaultstring:pnDefaultstring:*
  ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
 
 # Chuwi Hi10 Pro
@@ -214,6 +213,10 @@ sensor:modalias:acpi:KIOX000A*:dmi:*:svnConnect:pnTablet9:*
 sensor:modalias:acpi:KIOX000A*:dmi:*:svncube:pni1-TF:*
  ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
 
+# Cube i7
+sensor:modalias:acpi:SMO8500*:dmi:*:svncube:pni7:*
+ ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+
 # Cube i7 Stylus
 sensor:modalias:acpi:KIOX000A*:dmi:*:svnCube:pni7Stylus:*
  ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
@@ -389,7 +392,7 @@ sensor:modalias:acpi:KIOX000A*:dmi:*svnLAMINA:pnT-1016BNORD*
 sensor:modalias:acpi:NCPE0388*:dmi:*:rnLenovoYOGA510-14IKB:*
  ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, -1, 0; 0, 0, 1
 
-sensor:modalias:acpi:BOSC0200:BOSC0200:dmi:*ThinkPadYoga11e3rdGen*
+sensor:modalias:acpi:BOSC0200*:dmi:*ThinkPadYoga11e3rdGen*
  ACCEL_MOUNT_MATRIX=0, 1, 0; -1, 0, 0; 0, 0, 1
 
 # Miix3-1030
@@ -436,6 +439,12 @@ sensor:modalias:acpi:KIOX000A*:dmi:*:svnLINX*:pnLINX12*64:*
 #########################################
 # Medion
 #########################################
+
+# Medion Akoya E1239T MD60568
+sensor:modalias:acpi:KIOX0009*:dmi:*:svnMEDION:pnE1239TMD60568:*
+ ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+
+# Medion Akoya E2212T MD99720
 sensor:modalias:acpi:SMO8500*:dmi:*:svnMEDION:pnAkoyaE2212TMD99720:*
  ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
 
@@ -478,6 +487,10 @@ sensor:modalias:acpi:KIOX000A*:dmi:*:svnTMAX:pnTM800W560L:*
 sensor:modalias:acpi:KIOX000A*:dmi:*:svnTMAX:pnTM101W610L:*
  ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
 
+# Nuvision Encite Split 11. NES11-C432SSA
+sensor:modalias:acpi:BOSC0200*:dmi:*:svnNuvision:pnNES11:*
+ ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+
 #########################################
 # Onda
 #########################################
index 9830753831513e09a121d551b6a72336c7e4a574..bfd631504f617c79cfd8d19b2f676b8468ae87cc 100644 (file)
@@ -96,6 +96,7 @@
  <tr class="even"><td>Amazon Corporation</td><td>AMZN</td><td>02/06/2019</td> </tr>
  <tr class="odd"><td>ASEM S.p.A.</td><td>ASEM</td><td>04/29/2019</td> </tr>
  <tr class="even"><td>Fujitsu Limited</td><td>FUJI</td><td>06/18/2019</td> </tr>
+ <tr class="odd"><td>Phytium Technology Co. Ltd.</td><td>PHYT</td><td>02/14/2020</td> </tr>
       </tbody>
     </table>
   </body>
index f7aa100a09b172cb1b58051c362a3e9ebebc8525..e8169498ef843fcf433d8a252a49477885e594dc 100644 (file)
@@ -4388,12 +4388,6 @@ E820E2     (base 16)             HUMAX Co., Ltd.
                                Komagane  Nagano  399-4117\r
                                JP\r
 \r
-F4-EA-B5   (hex)               Aerohive Networks Inc.\r
-F4EAB5     (base 16)           Aerohive Networks Inc.\r
-                               1011 McCarthy Blvd\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
 8C-5B-F0   (hex)               ARRIS Group, Inc.\r
 8C5BF0     (base 16)           ARRIS Group, Inc.\r
                                6450 Sequence Drive\r
@@ -5096,12 +5090,6 @@ E470B8     (base 16)             Intel Corporate
                                Piscataway  NJ  08554\r
                                US\r
 \r
-00-86-9C   (hex)               Palo Alto Networks\r
-00869C     (base 16)           Palo Alto Networks\r
-                               4401 Great America Parkway\r
-                               Santa Clara  CA  95054\r
-                               US\r
-\r
 94-D9-B3   (hex)               TP-LINK TECHNOLOGIES CO.,LTD.\r
 94D9B3     (base 16)           TP-LINK TECHNOLOGIES CO.,LTD.\r
                                Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan\r
@@ -5390,12 +5378,6 @@ F46BEF     (base 16)             Sagemcom Broadband SAS
                                Rueil Malmaison Cedex  hauts de seine  92848\r
                                FR\r
 \r
-08-30-6B   (hex)               Palo Alto Networks\r
-08306B     (base 16)           Palo Alto Networks\r
-                               4401 Great America Parkway\r
-                               Santa Clara  CA  95054\r
-                               US\r
-\r
 10-CD-B6   (hex)               Essential Products, Inc.\r
 10CDB6     (base 16)           Essential Products, Inc.\r
                                380 Portage Avenue\r
@@ -5996,12 +5978,6 @@ D47DFC     (base 16)             TECNO MOBILE LIMITED
                                New Taipei City  Taiwan  231\r
                                TW\r
 \r
-00-06-31   (hex)               Calix Inc.\r
-000631     (base 16)           Calix Inc.\r
-                               2777 Orchard Parkway\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
 BC-2F-3D   (hex)               vivo Mobile Communication Co., Ltd.\r
 BC2F3D     (base 16)           vivo Mobile Communication Co., Ltd.\r
                                #283,BBK Road\r
@@ -6110,12 +6086,6 @@ F42B48     (base 16)             Ubiqam
                                 Longhua New District, Shenzhen  Guangdong  518012\r
                                CN\r
 \r
-54-03-84   (hex)               Hangkong Nano IC Technologies Co., Ltd\r
-540384     (base 16)           Hangkong Nano IC Technologies Co., Ltd\r
-                               Rm. 19C, Lockhart Ctr., 301-307 Lockhart Rd., Wan Chai, Hong Kong\r
-                               Hong Kong  Hong Kong  999077\r
-                               CN\r
-\r
 78-C1-A7   (hex)               zte corporation\r
 78C1A7     (base 16)           zte corporation\r
                                12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
@@ -8495,12 +8465,6 @@ F0EE58     (base 16)             PACE Telematics GmbH
                                Karlsruhe    76131\r
                                DE\r
 \r
-4C-A0-03   (hex)               T-21 Technologies LLC\r
-4CA003     (base 16)           T-21 Technologies LLC\r
-                               3733 University Blvd West Suite 307B\r
-                               Jacksonville  FL  32217\r
-                               US\r
-\r
 08-3F-BC   (hex)               zte corporation\r
 083FBC     (base 16)           zte corporation\r
                                12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
@@ -8693,12 +8657,6 @@ B0E2E5     (base 16)             Fiberhome Telecommunication Technologies Co.,LTD
                                Wuhan City  Hubei Province  430074\r
                                CN\r
 \r
-40-47-6A   (hex)               AG Acquisition Corp. d.b.a. ASTRO Gaming\r
-40476A     (base 16)           AG Acquisition Corp. d.b.a. ASTRO Gaming\r
-                               655 4th St.\r
-                               San Francisco  CA  94107\r
-                               US\r
-\r
 00-1F-A7   (hex)               Sony Interactive Entertainment Inc.\r
 001FA7     (base 16)           Sony Interactive Entertainment Inc.\r
                                1-7-1 Konan\r
@@ -10571,18 +10529,6 @@ C056E3     (base 16)           Hangzhou Hikvision Digital Technology Co.,Ltd.
                                Hangzhou  Zhejiang  310052\r
                                CN\r
 \r
-00-19-77   (hex)               Aerohive Networks Inc.\r
-001977     (base 16)           Aerohive Networks Inc.\r
-                               1011 McCarthy Blvd\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
-08-EA-44   (hex)               Aerohive Networks Inc.\r
-08EA44     (base 16)           Aerohive Networks Inc.\r
-                               1011 McCarthy Blvd\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
 EC-3E-F7   (hex)               Juniper Networks\r
 EC3EF7     (base 16)           Juniper Networks\r
                                1133 Innovation Way\r
@@ -11057,18 +11003,6 @@ D07AB5     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD
                                Brentwood  Essex  08854\r
                                GB\r
 \r
-CC-4E-24   (hex)               Brocade Communications Systems, Inc.\r
-CC4E24     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
-00-E0-52   (hex)               Brocade Communications Systems, Inc.\r
-00E052     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
 00-14-A4   (hex)               Hon Hai Precision Ind. Co.,Ltd.\r
 0014A4     (base 16)           Hon Hai Precision Ind. Co.,Ltd.\r
                                Building D21,No.1, East Zone 1st Road\r
@@ -11147,18 +11081,6 @@ C03E0F     (base 16)           BSkyB Ltd
                                Dongguan    523808\r
                                CN\r
 \r
-00-01-0F   (hex)               Brocade Communications Systems, Inc.\r
-00010F     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
-08-00-88   (hex)               Brocade Communications Systems, Inc.\r
-080088     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
 00-34-FE   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
 0034FE     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
                                No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
@@ -13550,12 +13472,6 @@ ECE512     (base 16)           tado GmbH
                                Seongnam-si  Gyeonggi-do  463-400\r
                                KR\r
 \r
-D4-CF-F9   (hex)               Shenzhen Sen5 Technology Co., Ltd.\r
-D4CFF9     (base 16)           Shenzhen Sen5 Technology Co., Ltd.\r
-                               3F (East), Block D, SDG Infoport, No.2 Kefeng Road,Nanshan District\r
-                               Shenzhen  Guangdong  518057\r
-                               CN\r
-\r
 48-82-44   (hex)               Life Fitness / Div. of Brunswick\r
 488244     (base 16)           Life Fitness / Div. of Brunswick\r
                                10601 W. Belmont Ave\r
@@ -13577,12 +13493,6 @@ C83168     (base 16)           eZEX corporation
 24-33-6C   (hex)               Private\r
 24336C     (base 16)           Private\r
 \r
-B8-7C-F2   (hex)               Aerohive Networks Inc.\r
-B87CF2     (base 16)           Aerohive Networks Inc.\r
-                               1011 McCarthy Blvd\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
 20-25-64   (hex)               PEGATRON CORPORATION\r
 202564     (base 16)           PEGATRON CORPORATION\r
                                5F No. 76, Ligong St., Beitou District\r
@@ -23726,12 +23636,6 @@ D4C766     (base 16)           Acentic GmbH
                                seoul    137-874\r
                                KR\r
 \r
-00-12-58   (hex)               Activis Polska\r
-001258     (base 16)           Activis Polska\r
-                               Swierzawska 5\r
-                               Poznan  Wielkopolska  60-321\r
-                               PL\r
-\r
 00-12-50   (hex)               Tokyo Aircaft Instrument Co., Ltd.\r
 001250     (base 16)           Tokyo Aircaft Instrument Co., Ltd.\r
                                1-35-1, Izumi-Honcho\r
@@ -32702,12 +32606,6 @@ F86FDE     (base 16)           Shenzhen Goodix Technology Co.,Ltd.
                                Shenzhen  Guangdong  518045\r
                                CN\r
 \r
-98-DF-82   (hex)               Hangzhou Hikvision Digital Technology Co.,Ltd.\r
-98DF82     (base 16)           Hangzhou Hikvision Digital Technology Co.,Ltd.\r
-                               No.555 Qianmo Road, Binjiang District\r
-                               Hangzhou  Zhejiang  310052\r
-                               CN\r
-\r
 64-66-24   (hex)               Sagemcom Broadband SAS\r
 646624     (base 16)           Sagemcom Broadband SAS\r
                                250, route de l'Empereur\r
@@ -33065,6 +32963,606 @@ A4FA76     (base 16)          New H3C Technologies Co., Ltd
                                NO.68, Qinghe Middle Street  Haidian District, Beijing  100085\r
                                CN\r
 \r
+D8-4D-B9   (hex)               Wu Qi Technologies,Inc.\r
+D84DB9     (base 16)           Wu Qi Technologies,Inc.\r
+                               14/F, 107 Middle Road, Xiantao Big Data Valley, Yubei District\r
+                               Chongqing  Chongqing  401120\r
+                               CN\r
+\r
+A0-4F-85   (hex)               LG Electronics (Mobile Communications)\r
+A04F85     (base 16)           LG Electronics (Mobile Communications)\r
+                               60-39, Gasan-dong, Geumcheon-gu\r
+                               Seoul    153-801\r
+                               KR\r
+\r
+D8-07-B6   (hex)               TP-LINK TECHNOLOGIES CO.,LTD.\r
+D807B6     (base 16)           TP-LINK TECHNOLOGIES CO.,LTD.\r
+                               Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan\r
+                               Shenzhen  Guangdong  518057\r
+                               CN\r
+\r
+64-6E-97   (hex)               TP-LINK TECHNOLOGIES CO.,LTD.\r
+646E97     (base 16)           TP-LINK TECHNOLOGIES CO.,LTD.\r
+                               Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan\r
+                               Shenzhen  Guangdong  518057\r
+                               CN\r
+\r
+78-50-7C   (hex)               Juniper Networks\r
+78507C     (base 16)           Juniper Networks\r
+                               1133 Innovation Way\r
+                               Sunnyvale  CA  94089\r
+                               US\r
+\r
+00-12-58   (hex)               TechVoIP Sp z o.o.\r
+001258     (base 16)           TechVoIP Sp z o.o.\r
+                               Os. Boleslawa Chrobrego 117 \r
+                               Poznan  Wielkopolska  60-681\r
+                               PL\r
+\r
+6C-16-32   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+6C1632     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+2C-1A-01   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+2C1A01     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+34-78-39   (hex)               zte corporation\r
+347839     (base 16)           zte corporation\r
+                               12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
+                               shenzhen  guangdong  518057\r
+                               CN\r
+\r
+24-16-9D   (hex)               Cisco Systems, Inc\r
+24169D     (base 16)           Cisco Systems, Inc\r
+                               80 West Tasman Drive\r
+                               San Jose  CA  94568\r
+                               US\r
+\r
+F4-19-E2   (hex)               Volterra\r
+F419E2     (base 16)           Volterra\r
+                               2550 Great America Way #350\r
+                               Santa Clara  CA  95054\r
+                               US\r
+\r
+4C-A0-03   (hex)               VITEC\r
+4CA003     (base 16)           VITEC\r
+                               99 rue Pierre Semard\r
+                               Chatillon    92320\r
+                               FR\r
+\r
+64-F2-FB   (hex)               Hangzhou Ezviz Software Co.,Ltd.\r
+64F2FB     (base 16)           Hangzhou Ezviz Software Co.,Ltd.\r
+                               Room 302, Unit B, Building 2, 399 Danfeng Road,Binjiang District\r
+                               Hangzhou  Zhejiang  310051\r
+                               CN\r
+\r
+30-80-9B   (hex)               New H3C Technologies Co., Ltd\r
+30809B     (base 16)           New H3C Technologies Co., Ltd\r
+                               466 Changhe Road, Binjiang District\r
+                               Hangzhou  Zhejiang  310052\r
+                               CN\r
+\r
+74-22-BB   (hex)               Huawei Device Co., Ltd.\r
+7422BB     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+6C-0D-34   (hex)               Nokia\r
+6C0D34     (base 16)           Nokia\r
+                               600 March Road\r
+                               Kanata  Ontario  K2K 2E6\r
+                               CA\r
+\r
+4C-45-76   (hex)               China Mobile(Hangzhou) Information Technology Co.,Ltd.\r
+4C4576     (base 16)           China Mobile(Hangzhou) Information Technology Co.,Ltd.\r
+                               No. 1600 Yuhangtang Road, Wuchang Street, Yuhang District\r
+                               Hangzhou  Zhejiang  310000\r
+                               CN\r
+\r
+B4-40-A4   (hex)               Apple, Inc.\r
+B440A4     (base 16)           Apple, Inc.\r
+                               1 Infinite Loop\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+48-B8-A3   (hex)               Apple, Inc.\r
+48B8A3     (base 16)           Apple, Inc.\r
+                               1 Infinite Loop\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+F4-DB-E3   (hex)               Apple, Inc.\r
+F4DBE3     (base 16)           Apple, Inc.\r
+                               1 Infinite Loop\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+BC-42-8C   (hex)               ALPS ELECTRIC CO., LTD.\r
+BC428C     (base 16)           ALPS ELECTRIC CO., LTD.\r
+                               nishida  6-1 \r
+                               Kakuda-City  Miyagi-Pref  981-1595\r
+                               JP\r
+\r
+F0-7C-C7   (hex)               Juniper Networks\r
+F07CC7     (base 16)           Juniper Networks\r
+                               1133 Innovation Way\r
+                               Sunnyvale  CA  94089\r
+                               US\r
+\r
+D4-5E-EC   (hex)               Beijing Xiaomi Electronics Co., Ltd.\r
+D45EEC     (base 16)           Beijing Xiaomi Electronics Co., Ltd.\r
+                               Building C, QingHe ShunShiJiaYe Technology Park, #66 ZhuFang Rd, HaiDian District\r
+                               Beijing  Beijing  10085\r
+                               CN\r
+\r
+74-C9-29   (hex)               Zhejiang Dahua Technology Co., Ltd.\r
+74C929     (base 16)           Zhejiang Dahua Technology Co., Ltd.\r
+                               No.1199,Waterfront Road \r
+                               Hangzhou  Zhejiang  310053\r
+                               CN\r
+\r
+D4-CF-F9   (hex)               Shenzhen SEI Robotics Co.,Ltd\r
+D4CFF9     (base 16)           Shenzhen SEI Robotics Co.,Ltd\r
+                               501,Productivity Building A, #5 Hi-Tech Middle 2nd Road\r
+                               Shenzhen  Guangdong  518057\r
+                               CN\r
+\r
+5C-B2-9E   (hex)               ASCO Power Technologies\r
+5CB29E     (base 16)           ASCO Power Technologies\r
+                               160 Park Avenue\r
+                               Florham Park  NJ  07932\r
+                               US\r
+\r
+9C-C9-EB   (hex)               NETGEAR\r
+9CC9EB     (base 16)           NETGEAR\r
+                               350 East Plumeria Drive\r
+                               San Jose  CA  95134\r
+                               US\r
+\r
+94-CC-04   (hex)               IEEE Registration Authority\r
+94CC04     (base 16)           IEEE Registration Authority\r
+                               445 Hoes Lane\r
+                               Piscataway  NJ  08554\r
+                               US\r
+\r
+90-EC-77   (hex)               silicom\r
+90EC77     (base 16)           silicom\r
+                               14 Atir-Yeda St/\r
+                               Kfar-Sava  Israel  44000\r
+                               IL\r
+\r
+88-C3-97   (hex)               Beijing Xiaomi Mobile Software Co., Ltd\r
+88C397     (base 16)           Beijing Xiaomi Mobile Software Co., Ltd\r
+                               The Rainbow City Office Building, 68 Qinghe Middle Street Haidian District\r
+                               Beijing  Beijing  100085\r
+                               CN\r
+\r
+F0-F6-C1   (hex)               Sonos, Inc.\r
+F0F6C1     (base 16)           Sonos, Inc.\r
+                               614 Chapala St\r
+                               Santa Barbara  CA  93101\r
+                               US\r
+\r
+60-68-4E   (hex)               Samsung Electronics Co.,Ltd\r
+60684E     (base 16)           Samsung Electronics Co.,Ltd\r
+                               #94-1, Imsoo-Dong\r
+                               Gumi  Gyeongbuk  730-350\r
+                               KR\r
+\r
+80-20-FD   (hex)               Samsung Electronics Co.,Ltd\r
+8020FD     (base 16)           Samsung Electronics Co.,Ltd\r
+                               #94-1, Imsoo-Dong\r
+                               Gumi  Gyeongbuk  730-350\r
+                               KR\r
+\r
+B4-CE-40   (hex)               Samsung Electronics Co.,Ltd\r
+B4CE40     (base 16)           Samsung Electronics Co.,Ltd\r
+                               #94-1, Imsoo-Dong\r
+                               Gumi  Gyeongbuk  730-350\r
+                               KR\r
+\r
+54-03-84   (hex)               Hongkong Nano IC Technologies Co., Ltd\r
+540384     (base 16)           Hongkong Nano IC Technologies Co., Ltd\r
+                               Rm. 19C, Lockhart Ctr., 301-307 Lockhart Rd., Wan Chai, Hong Kong\r
+                               Hong Kong  Hong Kong  999077\r
+                               CN\r
+\r
+04-BD-BF   (hex)               Samsung Electronics Co.,Ltd\r
+04BDBF     (base 16)           Samsung Electronics Co.,Ltd\r
+                               #94-1, Imsoo-Dong\r
+                               Gumi  Gyeongbuk  730-350\r
+                               KR\r
+\r
+BC-7A-BF   (hex)               Samsung Electronics Co.,Ltd\r
+BC7ABF     (base 16)           Samsung Electronics Co.,Ltd\r
+                               #94-1, Imsoo-Dong\r
+                               Gumi  Gyeongbuk  730-350\r
+                               KR\r
+\r
+B4-09-31   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+B40931     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+94-E7-EA   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+94E7EA     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+94-E4-BA   (hex)               Huawei Device Co., Ltd.\r
+94E4BA     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+34-71-46   (hex)               Huawei Device Co., Ltd.\r
+347146     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+2C-C5-46   (hex)               Huawei Device Co., Ltd.\r
+2CC546     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+0C-83-9A   (hex)               Huawei Device Co., Ltd.\r
+0C839A     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+E0-E0-FC   (hex)               Huawei Device Co., Ltd.\r
+E0E0FC     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+E0-D4-E8   (hex)               Intel Corporate\r
+E0D4E8     (base 16)           Intel Corporate\r
+                               Lot 8, Jalan Hi-Tech 2/3\r
+                               Kulim  Kedah  09000\r
+                               MY\r
+\r
+30-50-75   (hex)               GN Audio A/S\r
+305075     (base 16)           GN Audio A/S\r
+                               Lautrupbjerg 7\r
+                               Ballerup    DK-2750\r
+                               DK\r
+\r
+F4-B7-8D   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+F4B78D     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+A4-16-E7   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+A416E7     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+30-B9-B0   (hex)               Intracom Asia Co., Ltd\r
+30B9B0     (base 16)           Intracom Asia Co., Ltd\r
+                               4F., No77, Sec. 1, Xintai 5th Rd., Xizhi Dist.\r
+                               New Taipei City  Taiwan  221\r
+                               TW\r
+\r
+0C-35-FE   (hex)               Fiberhome Telecommunication Technologies Co.,LTD\r
+0C35FE     (base 16)           Fiberhome Telecommunication Technologies Co.,LTD\r
+                               No.5 DongXin Road\r
+                               Wuhan  Hubei  430074\r
+                               CN\r
+\r
+8C-83-DF   (hex)               Nokia\r
+8C83DF     (base 16)           Nokia\r
+                               600 March Road\r
+                               Kanata  Ontario  K2K 2E6\r
+                               CA\r
+\r
+AC-4B-1E   (hex)               Integri-Sys.Com LLC\r
+AC4B1E     (base 16)           Integri-Sys.Com LLC\r
+                               9130 South Dadeland Bvld. Suite 1509\r
+                               Miami  FL  33156\r
+                               US\r
+\r
+B0-E4-D5   (hex)               Google, Inc.\r
+B0E4D5     (base 16)           Google, Inc.\r
+                               1600 Amphitheatre Parkway\r
+                               Mountain View  CA  94043\r
+                               US\r
+\r
+D4-DA-CD   (hex)               BSkyB Ltd\r
+D4DACD     (base 16)           BSkyB Ltd\r
+                               130 Kings Road\r
+                               Brentwood  Essex  08854\r
+                               GB\r
+\r
+68-69-CA   (hex)               Hitachi, Ltd.\r
+6869CA     (base 16)           Hitachi, Ltd.\r
+                               27-18, Minami Oi 6-chome, Shinagawa-ku\r
+                               Tokyo    140-8572\r
+                               JP\r
+\r
+AC-4A-56   (hex)               Cisco Systems, Inc\r
+AC4A56     (base 16)           Cisco Systems, Inc\r
+                               80 West Tasman Drive\r
+                               San Jose  CA  94568\r
+                               US\r
+\r
+F4-EA-B5   (hex)               Extreme Networks, Inc.\r
+F4EAB5     (base 16)           Extreme Networks, Inc.\r
+                               1011 McCarthy Blvd\r
+                               Milpitas  CA  95035\r
+                               US\r
+\r
+00-19-77   (hex)               Extreme Networks, Inc.\r
+001977     (base 16)           Extreme Networks, Inc.\r
+                               1011 McCarthy Blvd\r
+                               Milpitas  CA  95035\r
+                               US\r
+\r
+08-EA-44   (hex)               Extreme Networks, Inc.\r
+08EA44     (base 16)           Extreme Networks, Inc.\r
+                               1011 McCarthy Blvd\r
+                               Milpitas  CA  95035\r
+                               US\r
+\r
+B0-B5-C3   (hex)               GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD\r
+B0B5C3     (base 16)           GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD\r
+                               NO.18 HAIBIN ROAD,\r
+                               DONG GUAN  GUANG DONG  523860\r
+                               CN\r
+\r
+70-4A-0E   (hex)               AMPAK Technology,Inc.\r
+704A0E     (base 16)           AMPAK Technology,Inc.\r
+                               3F, No.15-1 Zhonghua Road, Hsinchu Industrail Park, Hukou,\r
+                               Hsinchu  Hsinchu,Taiwan R.O.C.  30352\r
+                               TW\r
+\r
+08-30-6B   (hex)               Palo Alto Networks\r
+08306B     (base 16)           Palo Alto Networks\r
+                               3000 Tannery Way\r
+                               Santa Clara  CA  95054\r
+                               US\r
+\r
+00-86-9C   (hex)               Palo Alto Networks\r
+00869C     (base 16)           Palo Alto Networks\r
+                               3000 Tannery Way\r
+                               Santa Clara  CA  95054\r
+                               US\r
+\r
+4C-B9-11   (hex)               Raisecom Technology CO.,LTD\r
+4CB911     (base 16)           Raisecom Technology CO.,LTD\r
+                               No. 11, East Area, No. 10 Block, East Xibeiwang Road\r
+                               Beijing    100094\r
+                               CN\r
+\r
+40-47-6A   (hex)               Astro Gaming\r
+40476A     (base 16)           Astro Gaming\r
+                               340 Bryant St., Suite 101\r
+                               San Francisco  CA  94107\r
+                               US\r
+\r
+4C-CE-2D   (hex)               Danlaw Inc\r
+4CCE2D     (base 16)           Danlaw Inc\r
+                               23700 research Dr.\r
+                               Farmington Hills  MI  48335\r
+                               US\r
+\r
+BC-0F-9A   (hex)               D-Link International\r
+BC0F9A     (base 16)           D-Link International\r
+                               1 Internal Business Park, #03-12,The Synergy, Singapore\r
+                               Singapore  Singapore  609917\r
+                               SG\r
+\r
+30-B2-37   (hex)               GD Midea Air-Conditioning Equipment Co.,Ltd.\r
+30B237     (base 16)           GD Midea Air-Conditioning Equipment Co.,Ltd.\r
+                               Midea Global Innovation Center,Beijiao Town,Shunde\r
+                               Foshan  Guangdong  528311\r
+                               CN\r
+\r
+BC-5A-56   (hex)               Cisco Systems, Inc\r
+BC5A56     (base 16)           Cisco Systems, Inc\r
+                               80 West Tasman Drive\r
+                               San Jose  CA  94568\r
+                               US\r
+\r
+B8-7C-F2   (hex)               Extreme Networks, Inc.\r
+B87CF2     (base 16)           Extreme Networks, Inc.\r
+                               1011 McCarthy Blvd\r
+                               Milpitas  CA  95035\r
+                               US\r
+\r
+88-2B-94   (hex)               MADOKA SYSTEM Co.,Ltd.\r
+882B94     (base 16)           MADOKA SYSTEM Co.,Ltd.\r
+                               2-105 Hanasakidai Moriyama-ku\r
+                               Nagoya    463-0808\r
+                               JP\r
+\r
+7C-EF-61   (hex)               STR Elektronik Josef Schlechtinger GmbH\r
+7CEF61     (base 16)           STR Elektronik Josef Schlechtinger GmbH\r
+                               Auf dem Ohl 9\r
+                               Wenden    57482\r
+                               DE\r
+\r
+64-A9-65   (hex)               Linkflow Co., Ltd.\r
+64A965     (base 16)           Linkflow Co., Ltd.\r
+                               54, Nonhyeon-ro 2-gil, Gangnam-gu\r
+                               Seoul    06313\r
+                               KR\r
+\r
+24-62-CE   (hex)               Aruba, a Hewlett Packard Enterprise Company\r
+2462CE     (base 16)           Aruba, a Hewlett Packard Enterprise Company\r
+                               3333 Scott Blvd\r
+                               Santa Clara  CA  95054\r
+                               US\r
+\r
+68-E2-09   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+68E209     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+40-05-89   (hex)               T-Mobile, USA\r
+400589     (base 16)           T-Mobile, USA\r
+                               3625 132nd Ave SE\r
+                               BELLEVUE  WA  98006\r
+                               US\r
+\r
+C0-9B-F4   (hex)               IEEE Registration Authority\r
+C09BF4     (base 16)           IEEE Registration Authority\r
+                               445 Hoes Lane\r
+                               Piscataway  NJ  08554\r
+                               US\r
+\r
+F4-30-8B   (hex)               Xiaomi Communications Co Ltd\r
+F4308B     (base 16)           Xiaomi Communications Co Ltd\r
+                               The Rainbow City of China Resources\r
+                               NO.68, Qinghe Middle Street  Haidian District, Beijing  100085\r
+                               CN\r
+\r
+DC-6B-12   (hex)               worldcns inc.\r
+DC6B12     (base 16)           worldcns inc.\r
+                               174, Namjo-ro 1-gil, Jocheon-eup\r
+                               Jeju-si  Jeju-do  63335\r
+                               KR\r
+\r
+70-03-9F   (hex)               Espressif Inc.\r
+70039F     (base 16)           Espressif Inc.\r
+                               Room 204, Building 2, 690 Bibo Rd, Pudong New Area\r
+                               Shanghai  Shanghai  201203\r
+                               CN\r
+\r
+A0-DE-0F   (hex)               Huawei Device Co., Ltd.\r
+A0DE0F     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+F4-87-C5   (hex)               Huawei Device Co., Ltd.\r
+F487C5     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+08-00-88   (hex)               Brocade Communications Systems LLC\r
+080088     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+00-01-0F   (hex)               Brocade Communications Systems LLC\r
+00010F     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+08-B0-55   (hex)               ASKEY COMPUTER CORP\r
+08B055     (base 16)           ASKEY COMPUTER CORP\r
+                               10F,No.119,JIANKANG RD,ZHONGHE DIST\r
+                               NEW TAIPEI  TAIWAN  23585\r
+                               TW\r
+\r
+04-5F-B9   (hex)               Cisco Systems, Inc\r
+045FB9     (base 16)           Cisco Systems, Inc\r
+                               80 West Tasman Drive\r
+                               San Jose  CA  94568\r
+                               US\r
+\r
+CC-4E-24   (hex)               Brocade Communications Systems LLC\r
+CC4E24     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+00-E0-52   (hex)               Brocade Communications Systems LLC\r
+00E052     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+98-DF-82   (hex)               Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+98DF82     (base 16)           Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+                               No.555 Qianmo Road\r
+                               Hangzhou  Zhejiang  310052\r
+                               CN\r
+\r
+3C-F6-52   (hex)               zte corporation\r
+3CF652     (base 16)           zte corporation\r
+                               12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
+                               shenzhen  guangdong  518057\r
+                               CN\r
+\r
+5C-0F-FB   (hex)               Amino Communications Ltd\r
+5C0FFB     (base 16)           Amino Communications Ltd\r
+                               1010 Cambourne Business Park\r
+                               Cambourne  Cambs  CB23 6DP\r
+                               GB\r
+\r
+74-58-F3   (hex)               Amazon Technologies Inc.\r
+7458F3     (base 16)           Amazon Technologies Inc.\r
+                               P.O Box 8102\r
+                               Reno  NV  89507\r
+                               US\r
+\r
+00-06-31   (hex)               Calix Inc.\r
+000631     (base 16)           Calix Inc.\r
+                               2777 Orchard Pkwy\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+08-AA-55   (hex)               Motorola Mobility LLC, a Lenovo Company\r
+08AA55     (base 16)           Motorola Mobility LLC, a Lenovo Company\r
+                               222 West Merchandise Mart Plaza\r
+                               Chicago  IL  60654\r
+                               US\r
+\r
+C8-8B-E8   (hex)               Masimo Corporation\r
+C88BE8     (base 16)           Masimo Corporation\r
+                               40 Parker\r
+                               Irvine  CA  92618\r
+                               US\r
+\r
+40-40-28   (hex)               ZIV\r
+404028     (base 16)           ZIV\r
+                               Polígono Parque Tecnológico, 210\r
+                               ZAMUDIO  VIZCAYA  48170\r
+                               ES\r
+\r
+54-21-9D   (hex)               Samsung Electronics Co.,Ltd\r
+54219D     (base 16)           Samsung Electronics Co.,Ltd\r
+                               #94-1, Imsoo-Dong\r
+                               Gumi  Gyeongbuk  730-350\r
+                               KR\r
+\r
+A8-05-77   (hex)               Netlist, Inc.\r
+A80577     (base 16)           Netlist, Inc.\r
+                               175 Technology\r
+                               Irvine  CA  92618\r
+                               US\r
+\r
+F0-41-C6   (hex)               Heat Tech Company, Ltd.\r
+F041C6     (base 16)           Heat Tech Company, Ltd.\r
+                               221A, Tikhookeanskaya st.\r
+                               Khabarovsk    680033\r
+                               RU\r
+\r
+E4-3A-65   (hex)               MofiNetwork Inc\r
+E43A65     (base 16)           MofiNetwork Inc\r
+                               11 Boynton Cir\r
+                               Markham  Ontario  L6C 1A8\r
+                               CA\r
+\r
 9C-FF-C2   (hex)               AVI Systems GmbH\r
 9CFFC2     (base 16)           AVI Systems GmbH\r
                                Dr. Franz Wilhelmstraße 2A\r
@@ -34466,12 +34964,6 @@ ACA31E     (base 16)           Aruba, a Hewlett Packard Enterprise Company
                                Shenzhen  Guangdong  518057\r
                                CN\r
 \r
-F8-4D-FC   (hex)               Hangzhou Hikvision Digital Technology Co.,Ltd.\r
-F84DFC     (base 16)           Hangzhou Hikvision Digital Technology Co.,Ltd.\r
-                               No.555,qianmo road\r
-                               Hangzhou  Zhejiang  310052\r
-                               CN\r
-\r
 50-2B-98   (hex)               Es-tech International\r
 502B98     (base 16)           Es-tech International\r
                                228-70, Saneop-ro 155beon-gil, Gwonseon-gu, Suwon-si, Gyeonggi-do, Korea\r
@@ -35033,12 +35525,6 @@ FC8F7D     (base 16)           SHENZHEN GONGJIN ELECTRONICS CO.,LT
                                Kwai Chung  New Territories  000\r
                                CN\r
 \r
-BC-F3-10   (hex)               Aerohive Networks Inc.\r
-BCF310     (base 16)           Aerohive Networks Inc.\r
-                               1011 McCarthy Blvd\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
 B4-1D-2B   (hex)               Shenzhen YOUHUA Technology Co., Ltd\r
 B41D2B     (base 16)           Shenzhen YOUHUA Technology Co., Ltd\r
                                Room 407 Shenzhen University-town Business Park,Lishan Road,Taoyuan Street,Nanshan District\r
@@ -37187,12 +37673,6 @@ F079E8     (base 16)           GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
                                Dongguan    523808\r
                                CN\r
 \r
-08-66-1F   (hex)               Palo Alto Networks\r
-08661F     (base 16)           Palo Alto Networks\r
-                               4401 Great America Parkway\r
-                               Santa Clara  CA  95054\r
-                               US\r
-\r
 64-FB-50   (hex)               RoomReady/Zdi, Inc.\r
 64FB50     (base 16)           RoomReady/Zdi, Inc.\r
                                2200 N. Main Street\r
@@ -37475,12 +37955,6 @@ DCEB53     (base 16)           Wuhan QianXiao Elecronic Technology CO.,LTD
                                wuhan  hubei  430000\r
                                CN\r
 \r
-0C-B9-37   (hex)               Ubee Interactive Co., Limited\r
-0CB937     (base 16)           Ubee Interactive Co., Limited\r
-                               Room 1607 Dominion Centre, 43 Queen’s Road East\r
-                               Wanchai  Hong Kong  302\r
-                               HK\r
-\r
 EC-8A-C7   (hex)               Fiberhome Telecommunication Technologies Co.,LTD\r
 EC8AC7     (base 16)           Fiberhome Telecommunication Technologies Co.,LTD\r
                                No.5 DongXin Road\r
@@ -37523,12 +37997,6 @@ FC7F56     (base 16)           CoSyst Control Systems GmbH
                                Dongguan    523808\r
                                CN\r
 \r
-28-80-A2   (hex)               Novatel Wireless Solutions, Inc.\r
-2880A2     (base 16)           Novatel Wireless Solutions, Inc.\r
-                               9605 Scranton Road Suite 200\r
-                               San Diego    92121\r
-                               US\r
-\r
 78-81-02   (hex)               Sercomm Corporation.\r
 788102     (base 16)           Sercomm Corporation.\r
                                3F,No.81,Yu-Yih Rd.,Chu-Nan Chen\r
@@ -37793,12 +38261,6 @@ CC0677     (base 16)           Fiberhome Telecommunication Technologies Co.,LTD
                                New Taipei City  Taiwan  235\r
                                TW\r
 \r
-14-5E-45   (hex)               Kaleao Limited\r
-145E45     (base 16)           Kaleao Limited\r
-                               Sheraton House, Castle Park\r
-                               Cambridge  CAMBRIDGESHIRE  CB3 0AX\r
-                               GB\r
-\r
 54-D7-51   (hex)               Proximus\r
 54D751     (base 16)           Proximus\r
                                Bld du Roi Albert II 27\r
@@ -38831,12 +39293,6 @@ CC2D83     (base 16)           GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD
                                DONG GUAN  GUANG DONG  523860\r
                                CN\r
 \r
-00-15-FF   (hex)               Novatel Wireless Solutions, Inc.\r
-0015FF     (base 16)           Novatel Wireless Solutions, Inc.\r
-                               9605 Scranton Road Suite 200\r
-                               San Diego    92121\r
-                               US\r
-\r
 D4-6E-0E   (hex)               TP-LINK TECHNOLOGIES CO.,LTD.\r
 D46E0E     (base 16)           TP-LINK TECHNOLOGIES CO.,LTD.\r
                                Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan\r
@@ -38993,12 +39449,6 @@ E4186B     (base 16)           Zyxel Communications Corporation
                                Zory    44-240\r
                                PL\r
 \r
-D4-1D-71   (hex)               Palo Alto Networks\r
-D41D71     (base 16)           Palo Alto Networks\r
-                               4401 Great America Parkway\r
-                               Santa Clara  CA  95054\r
-                               US\r
-\r
 00-21-D1   (hex)               Samsung Electronics Co.,Ltd\r
 0021D1     (base 16)           Samsung Electronics Co.,Ltd\r
                                #94-1, Imsoo-Dong\r
@@ -39905,12 +40355,6 @@ E8886C     (base 16)           Shenzhen SC Technologies Co.,LTD
                                Shenzhen   Guangdong Province  518112\r
                                CN\r
 \r
-DC-35-F1   (hex)               Positivo Informática SA.\r
-DC35F1     (base 16)           Positivo Informática SA.\r
-                               Rua João Bettega, 5200\r
-                               Curitiba  Paraná  80730000\r
-                               BR\r
-\r
 00-24-FF   (hex)               QLogic Corporation\r
 0024FF     (base 16)           QLogic Corporation\r
                                26650 Aliso Viejo Parkway\r
@@ -42539,24 +42983,12 @@ B436A9     (base 16)          Fibocom Wireless Inc.
                                Dongguan  Guangdong  523808 \r
                                CN\r
 \r
-C8-66-5D   (hex)               Aerohive Networks Inc.\r
-C8665D     (base 16)           Aerohive Networks Inc.\r
-                               1011 McCarthy Blvd\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
 C8-47-8C   (hex)               Beken Corporation\r
 C8478C     (base 16)           Beken Corporation\r
                                Building 41, Capital of Tech Leaders, 1387 Zhangdong Road, Zhangjiang High-Tech Park, Pudong New District\r
                                Shanghai    201203\r
                                CN\r
 \r
-D8-54-A2   (hex)               Aerohive Networks Inc.\r
-D854A2     (base 16)           Aerohive Networks Inc.\r
-                               1011 McCarthy Blvd\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
 9C-EF-D5   (hex)               Panda Wireless, Inc.\r
 9CEFD5     (base 16)           Panda Wireless, Inc.\r
                                15559 Union Ave, Suite 300\r
@@ -42623,12 +43055,6 @@ E498D6     (base 16)           Apple, Inc.
                                Sunnyvale  CA  94089\r
                                US\r
 \r
-E0-1C-41   (hex)               Aerohive Networks Inc.\r
-E01C41     (base 16)           Aerohive Networks Inc.\r
-                               1011 McCarthy Blvd\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
 CC-A4-62   (hex)               ARRIS Group, Inc.\r
 CCA462     (base 16)           ARRIS Group, Inc.\r
                                6450 Sequence Drive\r
@@ -42965,12 +43391,6 @@ EC172F     (base 16)           TP-LINK TECHNOLOGIES CO.,LTD.
                                Shenzhen  Guangdong  518057\r
                                CN\r
 \r
-D8-1F-CC   (hex)               Brocade Communications Systems, Inc.\r
-D81FCC     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
 F4-31-C3   (hex)               Apple, Inc.\r
 F431C3     (base 16)           Apple, Inc.\r
                                1 Infinite Loop\r
@@ -43049,30 +43469,6 @@ D4C9B2     (base 16)           Quanergy Systems Inc
                                Chongqing  Chongqing  401332\r
                                CN\r
 \r
-00-27-F8   (hex)               Brocade Communications Systems, Inc.\r
-0027F8     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
-74-8E-F8   (hex)               Brocade Communications Systems, Inc.\r
-748EF8     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
-00-24-38   (hex)               Brocade Communications Systems, Inc.\r
-002438     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
-00-14-C9   (hex)               Brocade Communications Systems, Inc.\r
-0014C9     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
 14-E6-E4   (hex)               TP-LINK TECHNOLOGIES CO.,LTD.\r
 14E6E4     (base 16)           TP-LINK TECHNOLOGIES CO.,LTD.\r
                                Building 24 (floors 1,3,4,5) and 28 (floors1-4)  Central Science and Technology Park,Shennan Rd, Nanshan,\r
@@ -43103,12 +43499,6 @@ FCC897     (base 16)           zte corporation
                                Chongqing  Chongqing  401332\r
                                CN\r
 \r
-50-EB-1A   (hex)               Brocade Communications Systems, Inc.\r
-50EB1A     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
 00-18-82   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
 001882     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
                                F1-20,Section F,Huawei Base,Bantian, Longgang District\r
@@ -48152,12 +48542,6 @@ A47C14     (base 16)           ChargeStorm AB
                                Norrköping    60221\r
                                SE\r
 \r
-30-B2-16   (hex)               ABB AG - Power Grids - Grid Automation\r
-30B216     (base 16)           ABB AG - Power Grids - Grid Automation\r
-                               Kallstadter Strasse 1\r
-                               Mannheim    68309\r
-                               DE\r
-\r
 80-20-AF   (hex)               Trade FIDES, a.s.\r
 8020AF     (base 16)           Trade FIDES, a.s.\r
                                Dornych 57\r
@@ -51065,12 +51449,6 @@ D4AAFF     (base 16)           MICRO WORLD
                                Zirndorf    90513\r
                                DE\r
 \r
-00-26-74   (hex)               Electronic Solutions, Inc.\r
-002674     (base 16)           Electronic Solutions, Inc.\r
-                               1355 Horizon Avenue\r
-                               Lafayette  CO  80026\r
-                               US\r
-\r
 00-26-73   (hex)               RICOH COMPANY,LTD.\r
 002673     (base 16)           RICOH COMPANY,LTD.\r
                                810 Shimoimaizumi\r
@@ -58301,12 +58679,6 @@ D4AAFF     (base 16)           MICRO WORLD
                                Austin  TX  78730\r
                                US\r
 \r
-00-09-91   (hex)               GE Fanuc Automation Manufacturing, Inc.\r
-000991     (base 16)           GE Fanuc Automation Manufacturing, Inc.\r
-                               Route 606 & Route 29N\r
-                               Charlottesville  Virginia  22911\r
-                               US\r
-\r
 00-09-67   (hex)               Tachyon, Inc\r
 000967     (base 16)           Tachyon, Inc\r
                                9339 Carroll Park Drive\r
@@ -64433,12 +64805,6 @@ A85E45     (base 16)           ASUSTek COMPUTER INC.
                                Taipei  Taiwan  112\r
                                TW\r
 \r
-84-9A-40   (hex)               Hangzhou Hikvision Digital Technology Co.,Ltd.\r
-849A40     (base 16)           Hangzhou Hikvision Digital Technology Co.,Ltd.\r
-                               No.555 Qianmo Road, Binjiang District\r
-                               Hangzhou  Zhejiang  310052\r
-                               CN\r
-\r
 04-B1-A1   (hex)               Samsung Electronics Co.,Ltd\r
 04B1A1     (base 16)           Samsung Electronics Co.,Ltd\r
                                #94-1, Imsoo-Dong\r
@@ -65096,12 +65462,6 @@ D446E1     (base 16)           Apple, Inc.
                                Qingdao  Shandong  266510\r
                                CN\r
 \r
-48-7B-5E   (hex)               Social Mobile\r
-487B5E     (base 16)           Social Mobile\r
-                               16400 NW 2nd AVE suite 201, #201\r
-                               Miami  FL  33169\r
-                               US\r
-\r
 44-A5-6E   (hex)               NETGEAR\r
 44A56E     (base 16)           NETGEAR\r
                                350 East Plumeria Drive\r
@@ -65144,30 +65504,774 @@ A87EEA     (base 16)         Intel Corporate
                                Padova    35129\r
                                IT\r
 \r
-48-4E-FC   (hex)               ARRIS Group, Inc.\r
-484EFC     (base 16)           ARRIS Group, Inc.\r
-                               6450 Sequence Drive\r
-                               San Diego  CA  92121\r
-                               US\r
-\r
 2C-57-41   (hex)               Cisco Systems, Inc\r
 2C5741     (base 16)           Cisco Systems, Inc\r
                                80 West Tasman Drive\r
                                San Jose  CA  94568\r
                                US\r
 \r
+A8-4D-4A   (hex)               Audiowise Technology Inc.\r
+A84D4A     (base 16)           Audiowise Technology Inc.\r
+                               2F, No 1-1, Innovation RD I, Hsinchu Science Park\r
+                               Hsincu  Taiwan  30076\r
+                               TW\r
+\r
+78-94-E8   (hex)               Radio Bridge\r
+7894E8     (base 16)           Radio Bridge\r
+                               8601 73rd Ave N, Suite 38\r
+                               Brooklyn Park  MN  55428\r
+                               US\r
+\r
+48-4E-FC   (hex)               ARRIS Group, Inc.\r
+484EFC     (base 16)           ARRIS Group, Inc.\r
+                               6450 Sequence Drive\r
+                               San Diego  CA  92121\r
+                               US\r
+\r
 B0-B3-53   (hex)               IEEE Registration Authority\r
 B0B353     (base 16)           IEEE Registration Authority\r
                                445 Hoes Lane\r
                                Piscataway  NJ  08554\r
                                US\r
 \r
-A8-4D-4A   (hex)               Audiowise Technology Inc.\r
-A84D4A     (base 16)           Audiowise Technology Inc.\r
-                               2F, No 1-1, Innovation RD I, Hsinchu Science Park\r
-                               Hsincu  Taiwan  30076\r
+54-7F-BC   (hex)               iodyne\r
+547FBC     (base 16)           iodyne\r
+                               35 Miller Ave #175\r
+                               Mill Valley  CA  94941\r
+                               US\r
+\r
+7C-DF-A1   (hex)               Espressif Inc.\r
+7CDFA1     (base 16)           Espressif Inc.\r
+                               Room 204, Building 2, 690 Bibo Rd, Pudong New Area\r
+                               Shanghai  Shanghai  201203\r
+                               CN\r
+\r
+98-00-6A   (hex)               zte corporation\r
+98006A     (base 16)           zte corporation\r
+                               12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
+                               shenzhen  guangdong  518057\r
+                               CN\r
+\r
+00-26-74   (hex)               Hunter Douglas\r
+002674     (base 16)           Hunter Douglas\r
+                               One Duette Way\r
+                               Broomfield  CO  80020\r
+                               US\r
+\r
+1C-97-C5   (hex)               Ynomia Pty Ltd\r
+1C97C5     (base 16)           Ynomia Pty Ltd\r
+                               153 Tooronga Rd\r
+                               Glen Iris    3146\r
+                               AU\r
+\r
+5C-C1-D7   (hex)               Samsung Electronics Co.,Ltd\r
+5CC1D7     (base 16)           Samsung Electronics Co.,Ltd\r
+                               129, Samsung-ro, Youngtongl-Gu\r
+                               Suwon  Gyeonggi-Do  16677\r
+                               KR\r
+\r
+38-01-46   (hex)               SHENZHEN BILIAN ELECTRONIC CO.,LTD\r
+380146     (base 16)           SHENZHEN BILIAN ELECTRONIC CO.,LTD\r
+                               NO.268? Fuqian Rd, Jutang community, Guanlan Town, Longhua New district\r
+                               shenzhen  guangdong  518000\r
+                               CN\r
+\r
+88-96-55   (hex)               Zitte corporation\r
+889655     (base 16)           Zitte corporation\r
+                               4F Yokohama Kusunoki-cho Building,4-7 Kusunoki-cho,Nishi-ku\r
+                               Yokohama  Kanagawa  2200003\r
+                               JP\r
+\r
+F4-A4-D6   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+F4A4D6     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+FC-E1-4F   (hex)               BRK Brands, Inc.\r
+FCE14F     (base 16)           BRK Brands, Inc.\r
+                               3901 Liberty Street\r
+                               Aurora  IL  60504\r
+                               US\r
+\r
+74-B6-B6   (hex)               eero inc.\r
+74B6B6     (base 16)           eero inc.\r
+                               660 3rd Street\r
+                               San Francisco  CA  94107\r
+                               US\r
+\r
+EC-97-B2   (hex)               SUMEC Machinery & Electric Co.,Ltd.\r
+EC97B2     (base 16)           SUMEC Machinery & Electric Co.,Ltd.\r
+                               198# ChangJiang Road, XuanWu District, 17F, SUMEC Building\r
+                               Nanjing  JiangSu  210018\r
+                               CN\r
+\r
+28-FA-7A   (hex)               Zhejiang Tmall Technology Co., Ltd.\r
+28FA7A     (base 16)           Zhejiang Tmall Technology Co., Ltd.\r
+                               Ali Center,No.3331 Keyuan South RD (Shenzhen bay), Nanshan District, Shenzhen Guangdong province\r
+                               Shenzhen  GuangDong  518000\r
+                               CN\r
+\r
+84-2E-14   (hex)               Silicon Laboratories\r
+842E14     (base 16)           Silicon Laboratories\r
+                               7000 W. William Cannon Dr.\r
+                               Austin  TX  78735\r
+                               US\r
+\r
+10-05-E1   (hex)               Nokia\r
+1005E1     (base 16)           Nokia\r
+                               600 March Road\r
+                               Kanata  Ontario  K2K 2E6\r
+                               CA\r
+\r
+08-F4-58   (hex)               Huawei Device Co., Ltd.\r
+08F458     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+5C-BA-2C   (hex)               Hewlett Packard Enterprise\r
+5CBA2C     (base 16)           Hewlett Packard Enterprise\r
+                               8000 Foothills Blvd.\r
+                               Roseville  CA  95747\r
+                               US\r
+\r
+34-37-94   (hex)               Hamee Corp.\r
+343794     (base 16)           Hamee Corp.\r
+                               Square O2 2-12-10 Sakae-cho\r
+                               Odawara  Kanagawa  250-0011\r
+                               JP\r
+\r
+40-EC-99   (hex)               Intel Corporate\r
+40EC99     (base 16)           Intel Corporate\r
+                               Lot 8, Jalan Hi-Tech 2/3\r
+                               Kulim  Kedah  09000\r
+                               MY\r
+\r
+6C-D9-4C   (hex)               vivo Mobile Communication Co., Ltd.\r
+6CD94C     (base 16)           vivo Mobile Communication Co., Ltd.\r
+                               #283,BBK Road\r
+                               Wusha,Chang'An  DongGuan City,Guangdong,  523860\r
+                               CN\r
+\r
+EC-31-6D   (hex)               Hansgrohe\r
+EC316D     (base 16)           Hansgrohe\r
+                               Auestraße 5-9\r
+                               Schiltach    77761\r
+                               DE\r
+\r
+BC-54-2F   (hex)               Intel Corporate\r
+BC542F     (base 16)           Intel Corporate\r
+                               Lot 8, Jalan Hi-Tech 2/3\r
+                               Kulim  Kedah  09000\r
+                               MY\r
+\r
+44-10-FE   (hex)               Huizhou Foryou General Electronics Co., Ltd.\r
+4410FE     (base 16)           Huizhou Foryou General Electronics Co., Ltd.\r
+                               North Shangxia Road, Dongjiang Hi-tech Industry Park\r
+                               Huizhou  Guangdong  516000\r
+                               CN\r
+\r
+7C-AB-60   (hex)               Apple, Inc.\r
+7CAB60     (base 16)           Apple, Inc.\r
+                               1 Infinite Loop\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+44-C6-5D   (hex)               Apple, Inc.\r
+44C65D     (base 16)           Apple, Inc.\r
+                               1 Infinite Loop\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+18-7E-B9   (hex)               Apple, Inc.\r
+187EB9     (base 16)           Apple, Inc.\r
+                               1 Infinite Loop\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+4C-A6-4D   (hex)               Cisco Systems, Inc\r
+4CA64D     (base 16)           Cisco Systems, Inc\r
+                               80 West Tasman Drive\r
+                               San Jose  CA  94568\r
+                               US\r
+\r
+CC-7F-75   (hex)               Cisco Systems, Inc\r
+CC7F75     (base 16)           Cisco Systems, Inc\r
+                               80 West Tasman Drive\r
+                               San Jose  CA  94568\r
+                               US\r
+\r
+20-E8-74   (hex)               Apple, Inc.\r
+20E874     (base 16)           Apple, Inc.\r
+                               1 Infinite Loop\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+D0-3F-AA   (hex)               Apple, Inc.\r
+D03FAA     (base 16)           Apple, Inc.\r
+                               1 Infinite Loop\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+0C-B9-37   (hex)               Ubee Interactive Co., Limited\r
+0CB937     (base 16)           Ubee Interactive Co., Limited\r
+                               Flat/RM 1202, 12/F, AT Tower \r
+                               North Point  Hong Kong  180\r
+                               HK\r
+\r
+D4-DC-09   (hex)               Mist Systems, Inc.\r
+D4DC09     (base 16)           Mist Systems, Inc.\r
+                               1601 South De Anza Blvd, Suite 248\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+00-88-BA   (hex)               NC&C\r
+0088BA     (base 16)           NC&C\r
+                               Gurogu\r
+                               Seoul    08390\r
+                               KR\r
+\r
+F4-73-35   (hex)               Logitech Far East\r
+F47335     (base 16)           Logitech Far East\r
+                               #2 Creation Rd. 4,\r
+                               Hsinchu    300\r
                                TW\r
 \r
+90-AD-FC   (hex)               Telechips, Inc.\r
+90ADFC     (base 16)           Telechips, Inc.\r
+                               19F~23F,Luther Bldg.42, Olympic-ro 35da-gil, Songpa-gu,\r
+                               Seoul  Seoul  05510\r
+                               KR\r
+\r
+5C-A6-2D   (hex)               Cisco Systems, Inc\r
+5CA62D     (base 16)           Cisco Systems, Inc\r
+                               80 West Tasman Drive\r
+                               San Jose  CA  94568\r
+                               US\r
+\r
+40-2B-69   (hex)               Kumho Electric Inc.\r
+402B69     (base 16)           Kumho Electric Inc.\r
+                               309, Bongmu-ro, Namsa-myeon, Cheoin-gu\r
+                               Yongin-si  Gyeonggi-do  17118\r
+                               KR\r
+\r
+E8-E9-8E   (hex)               SOLAR controls s.r.o.\r
+E8E98E     (base 16)           SOLAR controls s.r.o.\r
+                               Brojova 25\r
+                               Plzen    32600\r
+                               CZ\r
+\r
+64-F6-BB   (hex)               Fibocom Wireless Inc.\r
+64F6BB     (base 16)           Fibocom Wireless Inc.\r
+                               5/F,TowerA,Technology Building 2,1057 Nanhai Blvd, Nanshan\r
+                               Shenzhen  518000  Guangdong\r
+                               CN\r
+\r
+BC-16-95   (hex)               zte corporation\r
+BC1695     (base 16)           zte corporation\r
+                               12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
+                               shenzhen  guangdong  518057\r
+                               CN\r
+\r
+DC-35-F1   (hex)               Positivo Tecnologia S.A.\r
+DC35F1     (base 16)           Positivo Tecnologia S.A.\r
+                               João Bettega, 5200\r
+                               Curitiba  Paraná  81350-000\r
+                               BR\r
+\r
+A4-08-01   (hex)               Amazon Technologies Inc.\r
+A40801     (base 16)           Amazon Technologies Inc.\r
+                               P.O Box 8102\r
+                               Reno  NV  89507\r
+                               US\r
+\r
+AC-1E-D0   (hex)               Temic Automotive Philippines Inc.\r
+AC1ED0     (base 16)           Temic Automotive Philippines Inc.\r
+                               Bagsakan Road, FTI estate\r
+                               Taguig    1630\r
+                               PH\r
+\r
+2C-EA-7F   (hex)               Dell Inc.\r
+2CEA7F     (base 16)           Dell Inc.\r
+                               One Dell Way\r
+                               Round Rock  TX  78682\r
+                               US\r
+\r
+34-51-80   (hex)               TCL King Electrical Appliances (Huizhou) Co., Ltd\r
+345180     (base 16)           TCL King Electrical Appliances (Huizhou) Co., Ltd\r
+                               10F, TCL Multimedia Building, TCL International E City, No.1001 Zhongshanyuan Rd., Nanshan District\r
+                               Shenzhen  Guangdong  518052\r
+                               CN\r
+\r
+A4-CF-D2   (hex)               Ubee Interactive Co., Limited\r
+A4CFD2     (base 16)           Ubee Interactive Co., Limited\r
+                               Flat/RM 1202, 12/F, AT Tower, 180 Electric Road\r
+                               North Point    00000\r
+                               HK\r
+\r
+A8-A0-97   (hex)               ScioTeq bvba\r
+A8A097     (base 16)           ScioTeq bvba\r
+                               President Kennedypark 35A\r
+                               Kortrijk    8500\r
+                               BE\r
+\r
+08-6B-D1   (hex)               Shenzhen SuperElectron Technology Co.,Ltd.\r
+086BD1     (base 16)           Shenzhen SuperElectron Technology Co.,Ltd.\r
+                               1213-1214, haosheng business center, dongbin road, nanshan street, nanshan district, shenzhen city\r
+                               Shenzhen  Guangdong  518000\r
+                               CN\r
+\r
+AC-3A-67   (hex)               Cisco Systems, Inc\r
+AC3A67     (base 16)           Cisco Systems, Inc\r
+                               80 West Tasman Drive\r
+                               San Jose  CA  94568\r
+                               US\r
+\r
+3C-B5-3D   (hex)               HUNAN GOKE MICROELECTRONICS CO.,LTD\r
+3CB53D     (base 16)           HUNAN GOKE MICROELECTRONICS CO.,LTD\r
+                               No.9, East 10th Road(South), Xingsha, Changsha\r
+                               Changsha  HUNAN  410131 \r
+                               CN\r
+\r
+98-0D-51   (hex)               Huawei Device Co., Ltd.\r
+980D51     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+00-AD-D5   (hex)               Huawei Device Co., Ltd.\r
+00ADD5     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+90-5D-7C   (hex)               New H3C Technologies Co., Ltd\r
+905D7C     (base 16)           New H3C Technologies Co., Ltd\r
+                               466 Changhe Road, Binjiang District\r
+                               Hangzhou  Zhejiang  310052\r
+                               CN\r
+\r
+98-FA-A7   (hex)               INNONET\r
+98FAA7     (base 16)           INNONET\r
+                               C-417, Munjeong Hyundai Knowledge Industry Center, Beobwon-ro 11-gil-7\r
+                               Songpa-gu  Seoul  05836\r
+                               KR\r
+\r
+48-7B-5E   (hex)               SMT TELECOMM HK\r
+487B5E     (base 16)           SMT TELECOMM HK\r
+                               Unit C 8/F Charmhill Centre 50 Hillwood RD.\r
+                               Tsim Sha Tsui  Kowloon  999077\r
+                               HK\r
+\r
+B8-E3-B1   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+B8E3B1     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+00-B7-A8   (hex)               Heinzinger electronic GmbH\r
+00B7A8     (base 16)           Heinzinger electronic GmbH\r
+                               Anton Jakob Str.4\r
+                               Rosenheim  BY  83026\r
+                               DE\r
+\r
+34-CF-F6   (hex)               Intel Corporate\r
+34CFF6     (base 16)           Intel Corporate\r
+                               Lot 8, Jalan Hi-Tech 2/3\r
+                               Kulim  Kedah  09000\r
+                               MY\r
+\r
+EC-79-49   (hex)               FUJITSU LIMITED\r
+EC7949     (base 16)           FUJITSU LIMITED\r
+                               403, Kosugi-cho 1-chome, Nakahara-ku\r
+                               Kawasaki  Kanagawa  211-0063\r
+                               JP\r
+\r
+D4-D2-D6   (hex)               FN-LINK TECHNOLOGY LIMITED\r
+D4D2D6     (base 16)           FN-LINK TECHNOLOGY LIMITED\r
+                               A Building,HuiXin industial park,No 31, YongHe road, Fuyong town, Bao'an District\r
+                               SHENZHEN  GUANGDONG  518100\r
+                               CN\r
+\r
+10-50-72   (hex)               Sercomm Corporation.\r
+105072     (base 16)           Sercomm Corporation.\r
+                               3F,No.81,Yu-Yih Rd.,Chu-Nan Chen\r
+                               Miao-Lih Hsuan    115\r
+                               TW\r
+\r
+90-0A-84   (hex)               Mellanox Technologies, Inc.\r
+900A84     (base 16)           Mellanox Technologies, Inc.\r
+                               350 Oakmead Parkway, Suite 100 \r
+                               Sunnyvale  CA  94085\r
+                               US\r
+\r
+AC-4A-67   (hex)               Cisco Systems, Inc\r
+AC4A67     (base 16)           Cisco Systems, Inc\r
+                               80 West Tasman Drive\r
+                               San Jose  CA  94568\r
+                               US\r
+\r
+70-CA-97   (hex)               Ruckus Wireless\r
+70CA97     (base 16)           Ruckus Wireless\r
+                               350 West Java Drive\r
+                               Sunnyvale  CA  94089\r
+                               US\r
+\r
+30-B2-16   (hex)               ABB Power Grids Germany AG – Grid Automation\r
+30B216     (base 16)           ABB Power Grids Germany AG – Grid Automation\r
+                               Kallstadter Strasse 1\r
+                               Mannheim    68309\r
+                               DE\r
+\r
+00-09-91   (hex)               Intelligent Platforms, LLC.\r
+000991     (base 16)           Intelligent Platforms, LLC.\r
+                               2500 Austin Drive\r
+                               Charlottesville  VA  22911\r
+                               US\r
+\r
+C8-66-5D   (hex)               Extreme Networks, Inc.\r
+C8665D     (base 16)           Extreme Networks, Inc.\r
+                               1011 McCarthy Blvd\r
+                               Milpitas  CA  95035\r
+                               US\r
+\r
+E0-1C-41   (hex)               Extreme Networks, Inc.\r
+E01C41     (base 16)           Extreme Networks, Inc.\r
+                               1011 McCarthy Blvd\r
+                               Milpitas  CA  95035\r
+                               US\r
+\r
+D8-54-A2   (hex)               Extreme Networks, Inc.\r
+D854A2     (base 16)           Extreme Networks, Inc.\r
+                               1011 McCarthy Blvd\r
+                               Milpitas  CA  95035\r
+                               US\r
+\r
+08-66-1F   (hex)               Palo Alto Networks\r
+08661F     (base 16)           Palo Alto Networks\r
+                               3000 Tannery Way\r
+                               Santa Clara  CA  95054\r
+                               US\r
+\r
+BC-F3-10   (hex)               Extreme Networks, Inc.\r
+BCF310     (base 16)           Extreme Networks, Inc.\r
+                               1011 McCarthy Blvd\r
+                               Milpitas  CA  95035\r
+                               US\r
+\r
+28-54-71   (hex)               Huawei Device Co., Ltd.\r
+285471     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+B8-8E-82   (hex)               Huawei Device Co., Ltd.\r
+B88E82     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+DC-33-3D   (hex)               Huawei Device Co., Ltd.\r
+DC333D     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+D8-78-7F   (hex)               Ubee Interactive Co., Limited\r
+D8787F     (base 16)           Ubee Interactive Co., Limited\r
+                               Flat/RM 1202, 12/F, AT Tower, 180 Electric Road\r
+                               North Point    00000\r
+                               HK\r
+\r
+D4-1D-71   (hex)               Palo Alto Networks\r
+D41D71     (base 16)           Palo Alto Networks\r
+                               3000 Tannery Way\r
+                               Santa Clara  CA  95054\r
+                               US\r
+\r
+08-87-C6   (hex)               INGRAM MICRO SERVICES\r
+0887C6     (base 16)           INGRAM MICRO SERVICES\r
+                               100 CHEMIN DE BAILLOT\r
+                               MONTAUBAN    82000\r
+                               FR\r
+\r
+78-57-73   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+785773     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+AC-60-89   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+AC6089     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+84-3E-92   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+843E92     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+70-8C-B6   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+708CB6     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+50-46-4A   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+50464A     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+C4-A4-02   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+C4A402     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+00-15-FF   (hex)               Novatel Wireless Solutions, Inc.\r
+0015FF     (base 16)           Novatel Wireless Solutions, Inc.\r
+                               9710 Scranton Rd., Suite 200\r
+                               San Diego  CA  92121\r
+                               US\r
+\r
+28-80-A2   (hex)               Novatel Wireless Solutions, Inc.\r
+2880A2     (base 16)           Novatel Wireless Solutions, Inc.\r
+                               9710 Scranton Rd., Suite 200\r
+                               San Diego  CA  92121\r
+                               US\r
+\r
+78-2B-46   (hex)               Intel Corporate\r
+782B46     (base 16)           Intel Corporate\r
+                               Lot 8, Jalan Hi-Tech 2/3\r
+                               Kulim  Kedah  09000\r
+                               MY\r
+\r
+68-33-2C   (hex)               KENSTEL NETWORKS LIMITED\r
+68332C     (base 16)           KENSTEL NETWORKS LIMITED\r
+                               34D SECTOR 57 HSIIDC INDUSTRIAL AREA PHASE 4\r
+                               KUNDLI  HARYANA  131028\r
+                               IN\r
+\r
+14-5E-45   (hex)               Bamboo Systems Group\r
+145E45     (base 16)           Bamboo Systems Group\r
+                               Sheraton House, Castle Park\r
+                               Cambridge  CAMBRIDGESHIRE  CB3 0AX\r
+                               GB\r
+\r
+70-61-7B   (hex)               Cisco Systems, Inc\r
+70617B     (base 16)           Cisco Systems, Inc\r
+                               80 West Tasman Drive\r
+                               San Jose  CA  94568\r
+                               US\r
+\r
+98-59-49   (hex)               LUXOTTICA GROUP S.P.A.\r
+985949     (base 16)           LUXOTTICA GROUP S.P.A.\r
+                               Piazzale Cadrona, 3\r
+                               Milano  MI  20132\r
+                               IT\r
+\r
+AC-67-B2   (hex)               Espressif Inc.\r
+AC67B2     (base 16)           Espressif Inc.\r
+                               Room 204, Building 2, 690 Bibo Rd, Pudong New Area\r
+                               Shanghai  Shanghai  201203\r
+                               CN\r
+\r
+9C-BD-6E   (hex)               DERA Co., Ltd\r
+9CBD6E     (base 16)           DERA Co., Ltd\r
+                               Zhichun road NO7 Building B Room1203 Haidian District\r
+                               Beijing    100191\r
+                               CN\r
+\r
+4C-33-29   (hex)               Sweroam\r
+4C3329     (base 16)           Sweroam\r
+                               Stortorget 16\r
+                               Orebro  N/A  70211\r
+                               SE\r
+\r
+64-E1-72   (hex)               Shenzhen Qihoo Intelligent Technology Co.,Ltd\r
+64E172     (base 16)           Shenzhen Qihoo Intelligent Technology Co.,Ltd\r
+                               Room 201,Block A.No.1,Qianwan Road1 Qianhai Shenzhen-HONGKONG Cooperation Zone\r
+                               Shenzhen  Guangdong  5181000\r
+                               CN\r
+\r
+48-8F-5A   (hex)               Routerboard.com\r
+488F5A     (base 16)           Routerboard.com\r
+                               Mikrotikls SIA\r
+                               Riga  Riga  LV1009\r
+                               LV\r
+\r
+10-06-45   (hex)               Sagemcom Broadband SAS\r
+100645     (base 16)           Sagemcom Broadband SAS\r
+                               250, route de l'Empereur\r
+                               Rueil Malmaison Cedex  hauts de seine  92848\r
+                               FR\r
+\r
+00-14-C9   (hex)               Brocade Communications Systems LLC\r
+0014C9     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+00-24-38   (hex)               Brocade Communications Systems LLC\r
+002438     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+74-8E-F8   (hex)               Brocade Communications Systems LLC\r
+748EF8     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+00-27-F8   (hex)               Brocade Communications Systems LLC\r
+0027F8     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+50-EB-1A   (hex)               Brocade Communications Systems LLC\r
+50EB1A     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+F8-4D-FC   (hex)               Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+F84DFC     (base 16)           Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+                               No.555 Qianmo Road\r
+                               Hangzhou  Zhejiang  310052\r
+                               CN\r
+\r
+84-9A-40   (hex)               Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+849A40     (base 16)           Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+                               No.555 Qianmo Road\r
+                               Hangzhou  Zhejiang  310052\r
+                               CN\r
+\r
+D8-1F-CC   (hex)               Brocade Communications Systems LLC\r
+D81FCC     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+C4-89-ED   (hex)               Solid Optics EU N.V.\r
+C489ED     (base 16)           Solid Optics EU N.V.\r
+                               De Huchtstraat 35\r
+                               Almere  Flevoland  1327 EC\r
+                               NL\r
+\r
+60-F4-3A   (hex)               Edifier International\r
+60F43A     (base 16)           Edifier International\r
+                               Suit 2207, 22nd floor, Tower II, Lippo centre, 89 Queensway\r
+                               Hong Kong    070\r
+                               CN\r
+\r
+58-A8-7B   (hex)               Fitbit, Inc.\r
+58A87B     (base 16)           Fitbit, Inc.\r
+                               199 Fremont Street, 14th Fl\r
+                               San Francisco  CA  94105\r
+                               US\r
+\r
+5C-6B-D7   (hex)               Foshan VIOMI Electric Appliance Technology Co. Ltd.\r
+5C6BD7     (base 16)           Foshan VIOMI Electric Appliance Technology Co. Ltd.\r
+                               No.2 North Xinxi Fourth Road, Xiashi Village Committee,Lunjiao Sub-district Office, Shunde District\r
+                               Foshan  Guandong  528308\r
+                               CN\r
+\r
+18-48-CA   (hex)               Murata Manufacturing Co., Ltd.\r
+1848CA     (base 16)           Murata Manufacturing Co., Ltd.\r
+                               1-10-1, Higashikotari\r
+                               Nagaokakyo-shi  Kyoto  617-8555\r
+                               JP\r
+\r
+90-EE-C7   (hex)               Samsung Electronics Co.,Ltd\r
+90EEC7     (base 16)           Samsung Electronics Co.,Ltd\r
+                               #94-1, Imsoo-Dong\r
+                               Gumi  Gyeongbuk  730-350\r
+                               KR\r
+\r
+10-29-AB   (hex)               Samsung Electronics Co.,Ltd\r
+1029AB     (base 16)           Samsung Electronics Co.,Ltd\r
+                               #94-1, Imsoo-Dong\r
+                               Gumi  Gyeongbuk  730-350\r
+                               KR\r
+\r
+18-4E-CB   (hex)               Samsung Electronics Co.,Ltd\r
+184ECB     (base 16)           Samsung Electronics Co.,Ltd\r
+                               #94-1, Imsoo-Dong\r
+                               Gumi  Gyeongbuk  730-350\r
+                               KR\r
+\r
+80-22-A7   (hex)               NEC Platforms, Ltd.\r
+8022A7     (base 16)           NEC Platforms, Ltd.\r
+                               2-3 Kandatsukasamachi\r
+                               Chiyodaku  Tokyo  101-8532\r
+                               JP\r
+\r
+64-E8-81   (hex)               Aruba, a Hewlett Packard Enterprise Company\r
+64E881     (base 16)           Aruba, a Hewlett Packard Enterprise Company\r
+                               3333 Scott Blvd\r
+                               Santa Clara  CA  95054\r
+                               US\r
+\r
+98-9D-5D   (hex)               Technicolor CH USA Inc.\r
+989D5D     (base 16)           Technicolor CH USA Inc.\r
+                               5030 Sugarloaf Parkway Bldg 6 \r
+                               Lawrenceville  GA  30044\r
+                               US\r
+\r
+5C-23-16   (hex)               Squirrels Research Labs LLC\r
+5C2316     (base 16)           Squirrels Research Labs LLC\r
+                               8050 Freedom Ave NW Suite B\r
+                               North Canton  OH  44720\r
+                               US\r
+\r
+04-21-44   (hex)               Sunitec Enterprise Co.,Ltd\r
+042144     (base 16)           Sunitec Enterprise Co.,Ltd\r
+                               3F.,No.98-1,Mincyuan Rd.Sindian City\r
+                               Taipei County 231    231141\r
+                               CN\r
+\r
+A0-27-B6   (hex)               Samsung Electronics Co.,Ltd\r
+A027B6     (base 16)           Samsung Electronics Co.,Ltd\r
+                               #94-1, Imsoo-Dong\r
+                               Gumi  Gyeongbuk  730-350\r
+                               KR\r
+\r
+10-39-17   (hex)               Samsung Electronics Co.,Ltd\r
+103917     (base 16)           Samsung Electronics Co.,Ltd\r
+                               #94-1, Imsoo-Dong\r
+                               Gumi  Gyeongbuk  730-350\r
+                               KR\r
+\r
+98-80-EE   (hex)               Samsung Electronics Co.,Ltd\r
+9880EE     (base 16)           Samsung Electronics Co.,Ltd\r
+                               #94-1, Imsoo-Dong\r
+                               Gumi  Gyeongbuk  730-350\r
+                               KR\r
+\r
+CC-0D-F2   (hex)               Motorola Mobility LLC, a Lenovo Company\r
+CC0DF2     (base 16)           Motorola Mobility LLC, a Lenovo Company\r
+                               222 West Merchandise Mart Plaza\r
+                               Chicago  IL  60654\r
+                               US\r
+\r
+94-BF-94   (hex)               Juniper Networks\r
+94BF94     (base 16)           Juniper Networks\r
+                               1133 Innovation Way\r
+                               Sunnyvale  CA  94089\r
+                               US\r
+\r
+94-43-4D   (hex)               Ciena Corporation\r
+94434D     (base 16)           Ciena Corporation\r
+                               7035 Ridge Road\r
+                               Hanover  MD  21076\r
+                               US\r
+\r
 84-80-94   (hex)               Meter, Inc.\r
 848094     (base 16)           Meter, Inc.\r
                                148 Townsend St\r
@@ -65324,12 +66428,6 @@ BC62D2     (base 16)           Genexis International B.V.
                                Wuhan  Hubei  430074\r
                                CN\r
 \r
-BC-BA-C2   (hex)               Hangzhou Hikvision Digital Technology Co.,Ltd.\r
-BCBAC2     (base 16)           Hangzhou Hikvision Digital Technology Co.,Ltd.\r
-                               No.555 Qianmo Road, Binjiang District\r
-                               Hangzhou  Zhejiang  310052\r
-                               CN\r
-\r
 44-D5-F2   (hex)               IEEE Registration Authority\r
 44D5F2     (base 16)           IEEE Registration Authority\r
                                445 Hoes Lane\r
@@ -66083,12 +67181,6 @@ FCAB90     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD
                                Dallas  TX  75243\r
                                US\r
 \r
-98-8B-0A   (hex)               Hangzhou Hikvision Digital Technology Co.,Ltd.\r
-988B0A     (base 16)           Hangzhou Hikvision Digital Technology Co.,Ltd.\r
-                               No.555 Qianmo Road, Binjiang District\r
-                               Hangzhou  Zhejiang  310052\r
-                               CN\r
-\r
 A4-97-5C   (hex)               VTech Telecommunications Ltd.\r
 A4975C     (base 16)           VTech Telecommunications Ltd.\r
                                23/F,Tai Ping Industrial Centre ,Block 1\r
@@ -67190,12 +68282,6 @@ F47DEF     (base 16)           Samsung Electronics Co.,Ltd
                                Hsichu  Taiwan  300\r
                                TW\r
 \r
-98-ED-5C   (hex)               Tesla Motors, Inc\r
-98ED5C     (base 16)           Tesla Motors, Inc\r
-                               3500 Deer Creek Road\r
-                               Palo Alto  CA  94304\r
-                               US\r
-\r
 78-70-52   (hex)               Welotec GmbH\r
 787052     (base 16)           Welotec GmbH\r
                                zum Hagenbach 7\r
@@ -69314,12 +70400,6 @@ B0FC36     (base 16)           CyberTAN Technology Inc.
                                SAN DIEGO  CA  92121\r
                                US\r
 \r
-34-85-84   (hex)               Aerohive Networks Inc.\r
-348584     (base 16)           Aerohive Networks Inc.\r
-                               1011 McCarthy Blvd\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
 00-19-C2   (hex)               Equustek Solutions, Inc.\r
 0019C2     (base 16)           Equustek Solutions, Inc.\r
                                #286 - 5489 Byrne Road,\r
@@ -70304,12 +71384,6 @@ D8DF7A     (base 16)           Quest Software, Inc.
                                Aliso Viejo  CA  92656\r
                                US\r
 \r
-E4-A7-49   (hex)               Palo Alto Networks\r
-E4A749     (base 16)           Palo Alto Networks\r
-                               4401 Great America Pkwy\r
-                               Santa Clara  CA  95054\r
-                               US\r
-\r
 14-5B-E1   (hex)               nyantec GmbH\r
 145BE1     (base 16)           nyantec GmbH\r
                                Europaplatz 2\r
@@ -71444,12 +72518,6 @@ EC0D9A     (base 16)           Mellanox Technologies, Inc.
                                Shanghai  Shanghai  201203\r
                                CN\r
 \r
-20-6C-8A   (hex)               Aerohive Networks Inc.\r
-206C8A     (base 16)           Aerohive Networks Inc.\r
-                               1011 McCarthy Blvd\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
 3C-FA-43   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
 3CFA43     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
                                No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
@@ -75446,12 +76514,6 @@ E83EFC     (base 16)           ARRIS Group, Inc.
                                San Diego  CA  92121\r
                                US\r
 \r
-40-18-B1   (hex)               Aerohive Networks Inc.\r
-4018B1     (base 16)           Aerohive Networks Inc.\r
-                               1011 McCarthy Blvd\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
 8C-09-F4   (hex)               ARRIS Group, Inc.\r
 8C09F4     (base 16)           ARRIS Group, Inc.\r
                                6450 Sequence Drive\r
@@ -75932,12 +76994,6 @@ D0154A     (base 16)           zte corporation
                                Brentwood  Essex  08854\r
                                GB\r
 \r
-C4-F5-7C   (hex)               Brocade Communications Systems, Inc.\r
-C4F57C     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
 14-B9-68   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
 14B968     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
                                No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
@@ -75950,18 +77006,6 @@ C4F57C     (base 16)           Brocade Communications Systems, Inc.
                                Dongguan    523808\r
                                CN\r
 \r
-00-12-F2   (hex)               Brocade Communications Systems, Inc.\r
-0012F2     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
-00-05-1E   (hex)               Brocade Communications Systems, Inc.\r
-00051E     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
 08-3E-8E   (hex)               Hon Hai Precision Ind. Co.,Ltd.\r
 083E8E     (base 16)           Hon Hai Precision Ind. Co.,Ltd.\r
                                Building D21,No.1, East Zone 1st Road\r
@@ -78596,12 +79640,6 @@ C89F1D     (base 16)           SHENZHEN COMMUNICATION TECHNOLOGIES CO.,LTD
                                Kunshan  Jiangsu  215300\r
                                CN\r
 \r
-88-5B-DD   (hex)               Aerohive Networks Inc.\r
-885BDD     (base 16)           Aerohive Networks Inc.\r
-                               1011 McCarthy Blvd\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
 08-74-F6   (hex)               Winterhalter Gastronom GmbH\r
 0874F6     (base 16)           Winterhalter Gastronom GmbH\r
                                Tettnanger Straße 72\r
@@ -80411,12 +81449,6 @@ F45214     (base 16)           Mellanox Technologies, Inc.
                                Bradenton  Florida  34203\r
                                US\r
 \r
-70-38-11   (hex)               Invensys Rail\r
-703811     (base 16)           Invensys Rail\r
-                               PO Box 79\r
-                               Chippenham  Wiltshire  SN15 1JD\r
-                               GB\r
-\r
 08-81-F4   (hex)               Juniper Networks\r
 0881F4     (base 16)           Juniper Networks\r
                                1133 Innovation Way\r
@@ -82079,12 +83111,6 @@ A4DB2E     (base 16)           Kingspan Environmental Ltd
                                Portadown  Armagh  BT63 5LF\r
                                GB\r
 \r
-60-54-64   (hex)               Eyedro Green Solutions Inc.\r
-605464     (base 16)           Eyedro Green Solutions Inc.\r
-                               151 Charles St W\r
-                               Kitchener  Ontario  N2G1H6\r
-                               CA\r
-\r
 C8-FE-30   (hex)               Bejing DAYO Mobile Communication Technology Ltd.\r
 C8FE30     (base 16)           Bejing DAYO Mobile Communication Technology Ltd.\r
                                Room 712, ULO Park Building No. 601E\r
@@ -94943,12 +95969,6 @@ D8D67E     (base 16)           GSK CNC EQUIPMENT CO.,LTD
                                WARRENDALE  PA  15086\r
                                US\r
 \r
-00-E0-EC   (hex)               CELESTICA INC.\r
-00E0EC     (base 16)           CELESTICA INC.\r
-                               844 DON MILLS ROAD\r
-                               NORTH YORK  ONTARIO M3C 1V7    \r
-                               CA\r
-\r
 00-E0-6C   (hex)               Ultra Electronics Command & Control Systems\r
 00E06C     (base 16)           Ultra Electronics Command & Control Systems\r
                                Knaves Beech Business Centre\r
@@ -95783,12 +96803,6 @@ D8D67E     (base 16)           GSK CNC EQUIPMENT CO.,LTD
                                CHATSWORTH  CA  91311\r
                                US\r
 \r
-00-20-A6   (hex)               Proxim Wireless\r
-0020A6     (base 16)           Proxim Wireless\r
-                               1561 Buckeye Drive\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
 00-20-2C   (hex)               WELLTRONIX CO., LTD.\r
 00202C     (base 16)           WELLTRONIX CO., LTD.\r
                                3F, NO. 36-1, HWANG HSI STREET\r
@@ -97835,48 +98849,636 @@ CC2D1B     (base 16)         SFR
                                ISSY LES MOULINEAUX    92130\r
                                FR\r
 \r
-80-30-49   (hex)               Liteon Technology Corporation\r
-803049     (base 16)           Liteon Technology Corporation\r
-                               4F, 90, Chien 1 Road\r
-                               New Taipei City  Taiwan  23585\r
+2C-F0-5D   (hex)               Micro-Star INTL CO., LTD.\r
+2CF05D     (base 16)           Micro-Star INTL CO., LTD.\r
+                               No.69, Lide St.,\r
+                               New Taipei City  Taiwan  235\r
                                TW\r
 \r
+94-3B-B0   (hex)               New H3C Technologies Co., Ltd\r
+943BB0     (base 16)           New H3C Technologies Co., Ltd\r
+                               466 Changhe Road, Binjiang District\r
+                               Hangzhou  Zhejiang  310052\r
+                               CN\r
+\r
 90-43-E2   (hex)               Cornami, Inc\r
 9043E2     (base 16)           Cornami, Inc\r
                                300 Orchard City Dr, Suite 131\r
                                Campbell  CA  95008\r
                                US\r
 \r
+80-30-49   (hex)               Liteon Technology Corporation\r
+803049     (base 16)           Liteon Technology Corporation\r
+                               4F, 90, Chien 1 Road\r
+                               New Taipei City  Taiwan  23585\r
+                               TW\r
+\r
+E8-49-43   (hex)               YUGE Information technology Co. Ltd\r
+E84943     (base 16)           YUGE Information technology Co. Ltd\r
+                               Room 303, Building No. 6, ShengRong Rd. 88, Pudong, Shanghai\r
+                               Shanghai    201203\r
+                               CN\r
+\r
+50-14-08   (hex)               AiNET\r
+501408     (base 16)           AiNET\r
+                               11700 MONTGOMERY RD\r
+                               BELTSVILLE  MD  20705-1159\r
+                               US\r
+\r
 28-9A-F7   (hex)               ADVA Optical Networking Ltd.\r
 289AF7     (base 16)           ADVA Optical Networking Ltd.\r
                                ADVAntage House\r
                                York    YO30 4RY\r
                                GB\r
 \r
-E8-49-43   (hex)               YUGE Information technology Co. Ltd\r
-E84943     (base 16)           YUGE Information technology Co. Ltd\r
-                               Room 303, Building No. 6, ShengRong Rd. 88, Pudong, Shanghai\r
+B0-B1-94   (hex)               zte corporation\r
+B0B194     (base 16)           zte corporation\r
+                               12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
+                               shenzhen  guangdong  518057\r
+                               CN\r
+\r
+10-C3-AB   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+10C3AB     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+28-11-EC   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+2811EC     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+E4-26-86   (hex)               DWnet Technologies(Suzhou) Corporation\r
+E42686     (base 16)           DWnet Technologies(Suzhou) Corporation\r
+                               No.8,Tangzhuang Road, Suzhou Industrial Park, Jiangsu, China\r
+                               Suzhou    21500\r
+                               CN\r
+\r
+00-69-2D   (hex)               Sunnovo International Limited\r
+00692D     (base 16)           Sunnovo International Limited\r
+                               1717 Haitai Building\r
+                               Beijing  Beijing  100083\r
+                               CN\r
+\r
+38-EB-47   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+38EB47     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+0C-37-96   (hex)               BIZLINK TECHNOLOGY, INC.\r
+0C3796     (base 16)           BIZLINK TECHNOLOGY, INC.\r
+                               47211 BAYSIDE PARKWAY\r
+                               Fremont  CA  94538\r
+                               US\r
+\r
+F4-03-2A   (hex)               Amazon Technologies Inc.\r
+F4032A     (base 16)           Amazon Technologies Inc.\r
+                               P.O Box 8102\r
+                               Reno  NV  89507\r
+                               US\r
+\r
+14-77-40   (hex)               Huawei Device Co., Ltd.\r
+147740     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+B4-15-7E   (hex)               Celona Inc.\r
+B4157E     (base 16)           Celona Inc.\r
+                               10061, Bubb Road Suite 300\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+18-D9-8F   (hex)               Huawei Device Co., Ltd.\r
+18D98F     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+64-5E-2C   (hex)               IRay Technology Co., Ltd.\r
+645E2C     (base 16)           IRay Technology Co., Ltd.\r
+                               11th Guiyang St.,\r
+                               Yantai  Shandong  264000\r
+                               CN\r
+\r
+00-E0-EC   (hex)               CELESTICA INC.\r
+00E0EC     (base 16)           CELESTICA INC.\r
+                               1900-5140 Yonge Street PO Box 42   \r
+                               Toronto  Ontario  M2N 6L7\r
+                               CA\r
+\r
+70-38-11   (hex)               Siemens Mobility Limited\r
+703811     (base 16)           Siemens Mobility Limited\r
+                               17 Langley Park Way\r
+                               Chippenham  Wiltshire  SN15 1GG\r
+                               GB\r
+\r
+64-62-66   (hex)               IEEE Registration Authority\r
+646266     (base 16)           IEEE Registration Authority\r
+                               445 Hoes Lane\r
+                               Piscataway  NJ  08554\r
+                               US\r
+\r
+48-7A-F6   (hex)               NCS ELECTRICAL SDN BHD\r
+487AF6     (base 16)           NCS ELECTRICAL SDN BHD\r
+                               NO. 20, 22, 24, 26, JALAN 1/3, RAWANG INTEGRATED INDUSTRIAL PARK, 48000 Rawang Selangor, MALAYSIA\r
+                               RAWANG  SELANGOR  48000\r
+                               MY\r
+\r
+48-6E-70   (hex)               Zhejiang Tmall Technology Co., Ltd.\r
+486E70     (base 16)           Zhejiang Tmall Technology Co., Ltd.\r
+                               Ali Center,No.3331 Keyuan South RD (Shenzhen bay), Nanshan District, Shenzhen Guangdong province\r
+                               Shenzhen  GuangDong  518000\r
+                               CN\r
+\r
+60-1D-9D   (hex)               Sichuan AI-Link Technology Co., Ltd.\r
+601D9D     (base 16)           Sichuan AI-Link Technology Co., Ltd.\r
+                               Anzhou, Industrial Park\r
+                               Mianyang  Sichuan  622650\r
+                               CN\r
+\r
+D8-5F-77   (hex)               Telink Semiconductor (Shanghai) Co., Ltd.\r
+D85F77     (base 16)           Telink Semiconductor (Shanghai) Co., Ltd.\r
+                               No. 1500 Zuchongzhi Rd, Building #3\r
                                Shanghai    201203\r
                                CN\r
 \r
-2C-F0-5D   (hex)               Micro-Star INTL CO., LTD.\r
-2CF05D     (base 16)           Micro-Star INTL CO., LTD.\r
-                               No.69, Lide St.,\r
-                               New Taipei City  Taiwan  235\r
+2C-97-ED   (hex)               Sony Imaging Products & Solutions Inc.\r
+2C97ED     (base 16)           Sony Imaging Products & Solutions Inc.\r
+                               konan 1-7-1\r
+                               minato-ku  Tokyo  108-0075\r
+                               JP\r
+\r
+20-82-6A   (hex)               GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD\r
+20826A     (base 16)           GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD\r
+                               NO.18 HAIBIN ROAD,\r
+                               DONG GUAN  GUANG DONG  523860\r
+                               CN\r
+\r
+B8-90-47   (hex)               Apple, Inc.\r
+B89047     (base 16)           Apple, Inc.\r
+                               1 Infinite Loop\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+90-9C-4A   (hex)               Apple, Inc.\r
+909C4A     (base 16)           Apple, Inc.\r
+                               1 Infinite Loop\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+90-8C-43   (hex)               Apple, Inc.\r
+908C43     (base 16)           Apple, Inc.\r
+                               1 Infinite Loop\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+18-8A-6A   (hex)               AVPro Global Hldgs\r
+188A6A     (base 16)           AVPro Global Hldgs\r
+                               2222 E 52nd Steeet N\r
+                               Sioux Falls  SD  57104\r
+                               US\r
+\r
+3C-7D-0A   (hex)               Apple, Inc.\r
+3C7D0A     (base 16)           Apple, Inc.\r
+                               1 Infinite Loop\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+D4-8A-39   (hex)               Samsung Electronics Co.,Ltd\r
+D48A39     (base 16)           Samsung Electronics Co.,Ltd\r
+                               #94-1, Imsoo-Dong\r
+                               Gumi  Gyeongbuk  730-350\r
+                               KR\r
+\r
+E4-F3-C4   (hex)               Samsung Electronics Co.,Ltd\r
+E4F3C4     (base 16)           Samsung Electronics Co.,Ltd\r
+                               #94-1, Imsoo-Dong\r
+                               Gumi  Gyeongbuk  730-350\r
+                               KR\r
+\r
+9C-2F-4E   (hex)               zte corporation\r
+9C2F4E     (base 16)           zte corporation\r
+                               12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
+                               shenzhen  guangdong  518057\r
+                               CN\r
+\r
+F0-10-90   (hex)               New H3C Technologies Co., Ltd\r
+F01090     (base 16)           New H3C Technologies Co., Ltd\r
+                               466 Changhe Road, Binjiang District\r
+                               Hangzhou  Zhejiang  310052\r
+                               CN\r
+\r
+08-BF-A0   (hex)               Samsung Electronics Co.,Ltd\r
+08BFA0     (base 16)           Samsung Electronics Co.,Ltd\r
+                               #94-1, Imsoo-Dong\r
+                               Gumi  Gyeongbuk  730-350\r
+                               KR\r
+\r
+70-1F-3C   (hex)               Samsung Electronics Co.,Ltd\r
+701F3C     (base 16)           Samsung Electronics Co.,Ltd\r
+                               #94-1, Imsoo-Dong\r
+                               Gumi  Gyeongbuk  730-350\r
+                               KR\r
+\r
+20-50-E7   (hex)               AMPAK Technology,Inc.\r
+2050E7     (base 16)           AMPAK Technology,Inc.\r
+                               3F, No.15-1 Zhonghua Road, Hsinchu Industrail Park, Hukou,\r
+                               Hsinchu  Hsinchu,Taiwan R.O.C.  30352\r
                                TW\r
 \r
-94-3B-B0   (hex)               New H3C Technologies Co., Ltd\r
-943BB0     (base 16)           New H3C Technologies Co., Ltd\r
+D8-A8-C8   (hex)               zte corporation\r
+D8A8C8     (base 16)           zte corporation\r
+                               12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
+                               shenzhen  guangdong  518057\r
+                               CN\r
+\r
+38-22-E2   (hex)               HP Inc.\r
+3822E2     (base 16)           HP Inc.\r
+                               10300 Energy Dr\r
+                               Spring  TX  77389\r
+                               US\r
+\r
+08-C0-EB   (hex)               Mellanox Technologies, Inc.\r
+08C0EB     (base 16)           Mellanox Technologies, Inc.\r
+                               350 Oakmead Parkway, Suite 100 \r
+                               Sunnyvale  CA  94085\r
+                               US\r
+\r
+E4-E1-12   (hex)               Texas Instruments\r
+E4E112     (base 16)           Texas Instruments\r
+                               12500 TI Blvd\r
+                               Dallas  TX  75243\r
+                               US\r
+\r
+34-14-B5   (hex)               Texas Instruments\r
+3414B5     (base 16)           Texas Instruments\r
+                               12500 TI Blvd\r
+                               Dallas  TX  75243\r
+                               US\r
+\r
+D0-03-EB   (hex)               Texas Instruments\r
+D003EB     (base 16)           Texas Instruments\r
+                               12500 TI Blvd\r
+                               Dallas  TX  75243\r
+                               US\r
+\r
+94-DB-56   (hex)               Sony Home Entertainment&Sound Products Inc\r
+94DB56     (base 16)           Sony Home Entertainment&Sound Products Inc\r
+                               Sony City Osaki 2-10-1 Osaki Shinagawa-ku\r
+                               Tokyo  Japan  141-8610\r
+                               JP\r
+\r
+88-9E-68   (hex)               Technicolor CH USA Inc.\r
+889E68     (base 16)           Technicolor CH USA Inc.\r
+                               5030 Sugarloaf Parkway Bldg 6 \r
+                               Lawrenceville  GA  30044\r
+                               US\r
+\r
+64-09-AC   (hex)               TCT mobile ltd\r
+6409AC     (base 16)           TCT mobile ltd\r
+                               No.86 hechang 7th road, zhongkai, Hi-Tech District\r
+                               Hui Zhou  Guang Dong  516006\r
+                               CN\r
+\r
+74-3A-EF   (hex)               Kaonmedia CO., LTD.\r
+743AEF     (base 16)           Kaonmedia CO., LTD.\r
+                               884-3, Seongnam-daero, Bundang-gu\r
+                               Seongnam-si  Gyeonggi-do  13517\r
+                               KR\r
+\r
+54-48-E6   (hex)               Beijing Xiaomi Mobile Software Co.,Ltd\r
+5448E6     (base 16)           Beijing Xiaomi Mobile Software Co.,Ltd\r
+                               Xiaomi Campus, No. 33 Xi erqi Middle Road, Haidian District\r
+                               Beijing  Beijing  100085\r
+                               CN\r
+\r
+18-9E-2C   (hex)               Huawei Device Co., Ltd.\r
+189E2C     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+5C-55-78   (hex)               iryx corp\r
+5C5578     (base 16)           iryx corp\r
+                               14 Hughes\r
+                               Irvine  CA  92618\r
+                               US\r
+\r
+6C-F7-12   (hex)               Nokia\r
+6CF712     (base 16)           Nokia\r
+                               Karaportti 3\r
+                               Espoo  Finland  02610\r
+                               FI\r
+\r
+4C-C5-3E   (hex)               Zyxel Communications Corporation\r
+4CC53E     (base 16)           Zyxel Communications Corporation\r
+                               No. 6 Innovation Road II, Science Park\r
+                               Hsichu  Taiwan  300\r
+                               TW\r
+\r
+90-56-FC   (hex)               TECNO MOBILE LIMITED\r
+9056FC     (base 16)           TECNO MOBILE LIMITED\r
+                               ROOMS 05-15, 13A/F., SOUTH TOWER, WORLD FINANCE CENTRE, HARBOUR CITY, 17 CANTON ROAD, TSIM SHA TSUI, KOWLOON, HONG KONG\r
+                               Hong Kong  Hong Kong  999077\r
+                               HK\r
+\r
+B8-63-92   (hex)               GUANGDONG GENIUS TECHNOLOGY CO., LTD.\r
+B86392     (base 16)           GUANGDONG GENIUS TECHNOLOGY CO., LTD.\r
+                               No.168, Middle Road Of East Gate\r
+                               Xiaobian Community  Chang'an Town  523851\r
+                               CN\r
+\r
+68-B9-D3   (hex)               Shenzhen Trolink Technology CO, LTD\r
+68B9D3     (base 16)           Shenzhen Trolink Technology CO, LTD\r
+                               201 B building 4 shijie, Chashu industry 505 block, Baoan airport Sanwei community, Hangcheng street Baoan area.\r
+                               Shenzhen  GuangDong  518000\r
+                               CN\r
+\r
+B8-C6-AA   (hex)               Earda Technologies co Ltd\r
+B8C6AA     (base 16)           Earda Technologies co Ltd\r
+                               Block A,Lianfeng Creative Park, #2 Jisheng Rd., Nansha District\r
+                               Guangzhou  Guangdong  511455\r
+                               CN\r
+\r
+54-AE-D0   (hex)               DASAN Networks, Inc. \r
+54AED0     (base 16)           DASAN Networks, Inc. \r
+                               DASAN Tower, 49, Daewangpangyo-ro, 644 Beon-gil, Bundang-gu\r
+                               Seongnam-si  Gyeonggi-do  13493\r
+                               KR\r
+\r
+10-70-FD   (hex)               Mellanox Technologies, Inc.\r
+1070FD     (base 16)           Mellanox Technologies, Inc.\r
+                               350 Oakmead Parkway, Suite 100 \r
+                               Sunnyvale  CA  94085\r
+                               US\r
+\r
+38-F6-01   (hex)               Solid State Storage Technology Corporation\r
+38F601     (base 16)           Solid State Storage Technology Corporation\r
+                               21F, 392, Ruey Kuang Road, Neihu\r
+                                Taipei    11492\r
+                               TW\r
+\r
+40-DE-AD   (hex)               Juniper Networks\r
+40DEAD     (base 16)           Juniper Networks\r
+                               1133 Innovation Way\r
+                               Sunnyvale  CA  94089\r
+                               US\r
+\r
+40-F5-20   (hex)               Espressif Inc.\r
+40F520     (base 16)           Espressif Inc.\r
+                               Room 204, Building 2, 690 Bibo Rd, Pudong New Area\r
+                               Shanghai  Shanghai  201203\r
+                               CN\r
+\r
+78-AA-82   (hex)               New H3C Technologies Co., Ltd\r
+78AA82     (base 16)           New H3C Technologies Co., Ltd\r
                                466 Changhe Road, Binjiang District\r
                                Hangzhou  Zhejiang  310052\r
                                CN\r
 \r
-50-14-08   (hex)               AiNET\r
-501408     (base 16)           AiNET\r
-                               11700 MONTGOMERY RD\r
-                               BELTSVILLE  MD  20705-1159\r
+20-6C-8A   (hex)               Extreme Networks, Inc.\r
+206C8A     (base 16)           Extreme Networks, Inc.\r
+                               1011 McCarthy Blvd\r
+                               Milpitas  CA  95035\r
+                               US\r
+\r
+34-85-84   (hex)               Extreme Networks, Inc.\r
+348584     (base 16)           Extreme Networks, Inc.\r
+                               1011 McCarthy Blvd\r
+                               Milpitas  CA  95035\r
+                               US\r
+\r
+40-18-B1   (hex)               Extreme Networks, Inc.\r
+4018B1     (base 16)           Extreme Networks, Inc.\r
+                               1011 McCarthy Blvd\r
+                               Milpitas  CA  95035\r
+                               US\r
+\r
+88-5B-DD   (hex)               Extreme Networks, Inc.\r
+885BDD     (base 16)           Extreme Networks, Inc.\r
+                               1011 McCarthy Blvd\r
+                               Milpitas  CA  95035\r
+                               US\r
+\r
+98-ED-5C   (hex)               Tesla,Inc.\r
+98ED5C     (base 16)           Tesla,Inc.\r
+                               3500 Deer Creek Road\r
+                               Palo Alto  CA  94304\r
+                               US\r
+\r
+C4-42-68   (hex)               CRESTRON ELECTRONICS, INC.\r
+C44268     (base 16)           CRESTRON ELECTRONICS, INC.\r
+                               15 Volvo Drive\r
+                               Rockleigh  NJ  07647\r
+                               US\r
+\r
+54-71-DD   (hex)               Huawei Device Co., Ltd.\r
+5471DD     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+E4-A7-49   (hex)               Palo Alto Networks\r
+E4A749     (base 16)           Palo Alto Networks\r
+                               3000 Tannery Way\r
+                               Santa Clara  CA  95054\r
+                               US\r
+\r
+84-D6-C5   (hex)               SolarEdge Technologies\r
+84D6C5     (base 16)           SolarEdge Technologies\r
+                               1 Abba Eban St.\r
+                               Herzelia    46725\r
+                               IL\r
+\r
+D8-91-36   (hex)               Dover Fueling Solutions\r
+D89136     (base 16)           Dover Fueling Solutions\r
+                               Industrieweg 5\r
+                               Bladel  NBR  5531AD\r
+                               NL\r
+\r
+C8-83-14   (hex)               Tempo Communications\r
+C88314     (base 16)           Tempo Communications\r
+                               1390 Aspen Way\r
+                               Vista  CA  92081\r
+                               US\r
+\r
+B8-E3-EE   (hex)               Universal Electronics, Inc.\r
+B8E3EE     (base 16)           Universal Electronics, Inc.\r
+                               201 E. Sandpointe Ave\r
+                               Santa Ana  CA  92707\r
+                               US\r
+\r
+24-C8-D3   (hex)               McWane India Pvt Ltd\r
+24C8D3     (base 16)           McWane India Pvt Ltd\r
+                               483, Kamaraj Road, Upplipalayam\r
+                               Coimbatore  Tamil Nadu  641015\r
+                               IN\r
+\r
+60-54-64   (hex)               Eyedro Green Solutions Inc.\r
+605464     (base 16)           Eyedro Green Solutions Inc.\r
+                               130 Weber St W, Suite 201\r
+                               Kitchener  Ontario  N2H4A2\r
+                               CA\r
+\r
+FC-95-6A   (hex)               OCTAGON SYSTEMS CORP.\r
+FC956A     (base 16)           OCTAGON SYSTEMS CORP.\r
+                               7403 Church Ranch Blvd\r
+                               Westminster  CO  80021\r
+                               US\r
+\r
+40-B3-1E   (hex)               Universal Electronics, Inc.\r
+40B31E     (base 16)           Universal Electronics, Inc.\r
+                               201 E. Sandpointe Ave\r
+                               Santa Ana  CA  92707\r
+                               US\r
+\r
+88-40-33   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+884033     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+E0-F6-B5   (hex)               Nintendo Co.,Ltd\r
+E0F6B5     (base 16)           Nintendo Co.,Ltd\r
+                               11-1 HOKOTATE-CHO KAMITOBA,MINAMI-KU\r
+                               KYOTO  KYOTO  601-8501\r
+                               JP\r
+\r
+AC-7A-56   (hex)               Cisco Systems, Inc\r
+AC7A56     (base 16)           Cisco Systems, Inc\r
+                               80 West Tasman Drive\r
+                               San Jose  CA  94568\r
+                               US\r
+\r
+44-8D-BF   (hex)               Rhino Mobility LLC\r
+448DBF     (base 16)           Rhino Mobility LLC\r
+                               8 The Green, Suite A\r
+                               Dover  DE  19901\r
+                               US\r
+\r
+94-8A-C6   (hex)               Realme Chongqing Mobile Telecommunications Corp.,Ltd.\r
+948AC6     (base 16)           Realme Chongqing Mobile Telecommunications Corp.,Ltd.\r
+                               No.178 Yulong Avenue, Yufengshan, Yubei District, Chongqing.\r
+                               Chongqing   China  401120\r
+                               CN\r
+\r
+C0-A6-6D   (hex)               Inspur Group Co., Ltd.\r
+C0A66D     (base 16)           Inspur Group Co., Ltd.\r
+                               No.1036 Langchao Rd.\r
+                               Jinan  Shandong  250101\r
+                               CN\r
+\r
+70-F0-96   (hex)               Cisco Systems, Inc\r
+70F096     (base 16)           Cisco Systems, Inc\r
+                               80 West Tasman Drive\r
+                               San Jose  CA  94568\r
+                               US\r
+\r
+00-20-A6   (hex)               Proxim Wireless\r
+0020A6     (base 16)           Proxim Wireless\r
+                               2114 Ringwood Ave\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+58-50-ED   (hex)               Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+5850ED     (base 16)           Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+                               No.555 Qianmo Road\r
+                               Hangzhou  Zhejiang  310052\r
+                               CN\r
+\r
+00-05-1E   (hex)               Brocade Communications Systems LLC\r
+00051E     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
                                US\r
 \r
+98-8B-0A   (hex)               Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+988B0A     (base 16)           Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+                               No.555 Qianmo Road\r
+                               Hangzhou  Zhejiang  310052\r
+                               CN\r
+\r
+BC-BA-C2   (hex)               Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+BCBAC2     (base 16)           Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+                               No.555 Qianmo Road\r
+                               Hangzhou  Zhejiang  310052\r
+                               CN\r
+\r
+AC-CB-51   (hex)               Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+ACCB51     (base 16)           Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+                               No.555 Qianmo Road\r
+                               Hangzhou  Zhejiang  310052\r
+                               CN\r
+\r
+E0-D4-62   (hex)               Huawei Device Co., Ltd.\r
+E0D462     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+90-98-38   (hex)               Huawei Device Co., Ltd.\r
+909838     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+84-CC-A8   (hex)               Espressif Inc.\r
+84CCA8     (base 16)           Espressif Inc.\r
+                               Room 204, Building 2, 690 Bibo Rd, Pudong New Area\r
+                               Shanghai  Shanghai  201203\r
+                               CN\r
+\r
+C4-F5-7C   (hex)               Brocade Communications Systems LLC\r
+C4F57C     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+00-12-F2   (hex)               Brocade Communications Systems LLC\r
+0012F2     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+6C-2F-8A   (hex)               Samsung Electronics Co.,Ltd\r
+6C2F8A     (base 16)           Samsung Electronics Co.,Ltd\r
+                               #94-1, Imsoo-Dong\r
+                               Gumi  Gyeongbuk  730-350\r
+                               KR\r
+\r
+F0-B0-22   (hex)               TOHO Electronics INC.\r
+F0B022     (base 16)           TOHO Electronics INC.\r
+                               1-13-21 Tanashioda, Chuo-Ku\r
+                               Sagamihara-City  Kanagawa  252-0245\r
+                               JP\r
+\r
+98-7E-CA   (hex)               Inventus Power Eletronica do Brasil LTDA\r
+987ECA     (base 16)           Inventus Power Eletronica do Brasil LTDA\r
+                               Av Buriti, 4285 Distrito Industrial\r
+                               Manaus  Amazonas  69075000\r
+                               BR\r
+\r
+2C-9F-FB   (hex)               Wistron Neweb Corporation\r
+2C9FFB     (base 16)           Wistron Neweb Corporation\r
+                               No.20,Park Avenue II,Hsinchu Science Park\r
+                               Hsin-Chu  R.O.C.  308\r
+                               TW\r
+\r
+50-38-2F   (hex)               ASE Group Chung-Li\r
+50382F     (base 16)           ASE Group Chung-Li\r
+                               No 550,Chung-Hwa Road Section1\r
+                               Chung-Li , Taoyuan  Taoyuan  32016\r
+                               TW\r
+\r
+0C-14-D2   (hex)               China Mobile Group Device Co.,Ltd.\r
+0C14D2     (base 16)           China Mobile Group Device Co.,Ltd.\r
+                               32 Xuanwumen West Street,Xicheng District\r
+                               Beijing    100053\r
+                               CN\r
+\r
 F8-D0-27   (hex)               Seiko Epson Corporation\r
 F8D027     (base 16)           Seiko Epson Corporation\r
                                2070 Kotobuki Koaka\r
@@ -103013,12 +104615,6 @@ B8224F     (base 16)         SICHUAN TIANYI COMHEART TELECOMCO., LTD
                                Pleasanton  CA  94588\r
                                US\r
 \r
-CC-BE-59   (hex)               Calix Inc.\r
-CCBE59     (base 16)           Calix Inc.\r
-                               2777 Orchard Parkway\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
 F8-A3-4F   (hex)               zte corporation\r
 F8A34F     (base 16)           zte corporation\r
                                12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
@@ -107357,12 +108953,6 @@ F88FCA     (base 16)         Google, Inc.
                                Giheung-gu, Yongin-City Kyungki-do    446-599\r
                                KR\r
 \r
-C8-67-5E   (hex)               Aerohive Networks Inc.\r
-C8675E     (base 16)           Aerohive Networks Inc.\r
-                               1011 McCarthy Blvd\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
 94-86-CD   (hex)               SEOUL ELECTRONICS&TELECOM\r
 9486CD     (base 16)           SEOUL ELECTRONICS&TELECOM\r
                                709, Namkwangcentrex 440-4, Cheongcheon-dong, \r
@@ -107387,24 +108977,6 @@ C8675E     (base 16)         Aerohive Networks Inc.
                                Hangzhou  Zhejiang  310052\r
                                CN\r
 \r
-F0-9C-E9   (hex)               Aerohive Networks Inc.\r
-F09CE9     (base 16)           Aerohive Networks Inc.\r
-                               1011 McCarthy Blvd\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
-9C-5D-12   (hex)               Aerohive Networks Inc.\r
-9C5D12     (base 16)           Aerohive Networks Inc.\r
-                               1011 McCarthy Blvd\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
-C4-13-E2   (hex)               Aerohive Networks Inc.\r
-C413E2     (base 16)           Aerohive Networks Inc.\r
-                               1011 McCarthy Blvd\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
 68-DB-CA   (hex)               Apple, Inc.\r
 68DBCA     (base 16)           Apple, Inc.\r
                                1 Infinite Loop\r
@@ -107873,36 +109445,6 @@ A4516F     (base 16)         Microsoft Mobile Oy
                                Chongqing  Chongqing  401332\r
                                CN\r
 \r
-00-04-80   (hex)               Brocade Communications Systems, Inc.\r
-000480     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
-00-0C-DB   (hex)               Brocade Communications Systems, Inc.\r
-000CDB     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
-00-1B-ED   (hex)               Brocade Communications Systems, Inc.\r
-001BED     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
-00-05-33   (hex)               Brocade Communications Systems, Inc.\r
-000533     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
-00-60-69   (hex)               Brocade Communications Systems, Inc.\r
-006069     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
 08-18-1A   (hex)               zte corporation\r
 08181A     (base 16)           zte corporation\r
                                12/F ZTE Plaza,Keji Road South,Hi-Tech Industrial Park,Nanshan District,\r
@@ -108023,18 +109565,6 @@ EC26CA     (base 16)         TP-LINK TECHNOLOGIES CO.,LTD.
                                Nagaokakyo-shi  Kyoto  617-8555\r
                                JP\r
 \r
-00-60-DF   (hex)               Brocade Communications Systems, Inc.\r
-0060DF     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
-00-00-88   (hex)               Brocade Communications Systems, Inc.\r
-000088     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
 F4-55-9C   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
 F4559C     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
                                Building 17,Nangang Industrial Park, Tangtou 3nd Industrial Estate,Shiyan Baoan District \r
@@ -109589,12 +111119,6 @@ C02567     (base 16)         Nexxt Solutions
                                MIAMI  FLORIDA  33178\r
                                US\r
 \r
-60-9C-9F   (hex)               Brocade Communications Systems, Inc.\r
-609C9F     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
 A8-82-7F   (hex)               CIBN Oriental Network(Beijing) CO.,Ltd\r
 A8827F     (base 16)           CIBN Oriental Network(Beijing) CO.,Ltd\r
                                Floor 18B,Block B(International Resources Building),No.18B(Wanda Plaza),Shijingshan Road,Shijingshan,Beijing(100043)\r
@@ -112676,12 +114200,6 @@ BC0200     (base 16)         Stewart Audio
                                Mountain View  CA  94041\r
                                US\r
 \r
-9C-61-1D   (hex)               Omni-ID USA, Inc.\r
-9C611D     (base 16)           Omni-ID USA, Inc.\r
-                               1200 Ridgeway Ave\r
-                               Rochester  NY  14615\r
-                               US\r
-\r
 48-91-53   (hex)               Weinmann Geräte für Medizin GmbH + Co. KG\r
 489153     (base 16)           Weinmann Geräte für Medizin GmbH + Co. KG\r
                                Kronsaalsweg 40\r
@@ -122639,12 +124157,6 @@ A07332     (base 16)         Cashmaster International Limited
                                South Lawrence  MA  01843\r
                                US\r
 \r
-00-0C-32   (hex)               Avionic Design Development GmbH\r
-000C32     (base 16)           Avionic Design Development GmbH\r
-                               Sthamerstrasse 24a\r
-                               Hamburg    22397\r
-                               DE\r
-\r
 00-0C-33   (hex)               Compucase Enterprise Co. Ltd.\r
 000C33     (base 16)           Compucase Enterprise Co. Ltd.\r
                                225 Lane 54, An Ho Road, Section 2nd.\r
@@ -123362,12 +124874,6 @@ A07332     (base 16)         Cashmaster International Limited
                                San Jose  CA  95119\r
                                US\r
 \r
-00-0A-0D   (hex)               FCI Deutschland GmbH\r
-000A0D     (base 16)           FCI Deutschland GmbH\r
-                               Holzhauser Strasse 175\r
-                               13509 Berlin    \r
-                               DE\r
-\r
 00-09-A9   (hex)               Ikanos Communications\r
 0009A9     (base 16)           Ikanos Communications\r
                                47709 Fremont Blvd\r
@@ -129575,12 +131081,6 @@ D4F5EF     (base 16)         Hewlett Packard Enterprise
                                Roseville  CA  95747\r
                                US\r
 \r
-90-B8-32   (hex)               Aerohive Networks Inc.\r
-90B832     (base 16)           Aerohive Networks Inc.\r
-                               1011 McCarthy Blvd\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
 28-BD-89   (hex)               Google, Inc.\r
 28BD89     (base 16)           Google, Inc.\r
                                1600 Amphitheatre Parkway\r
@@ -130232,18 +131732,684 @@ ACF8CC     (base 16)               ARRIS Group, Inc.
                                REDMOND    98052\r
                                US\r
 \r
+CC-D4-2E   (hex)               Arcadyan Corporation\r
+CCD42E     (base 16)           Arcadyan Corporation\r
+                               No.8, Sec.2, Guangfu Rd.\r
+                               Hsinchu City  Hsinchu  30071\r
+                               TW\r
+\r
 C8-53-E1   (hex)               Beijing Bytedance Network Technology Co., Ltd\r
 C853E1     (base 16)           Beijing Bytedance Network Technology Co., Ltd\r
                                No.1 Building, Zhonghang Square, West Road of the Northern 3rd Circuit, Haidian Distrct\r
                                Beijing  Beijing  100098\r
                                CN\r
 \r
+14-16-9D   (hex)               Cisco Systems, Inc\r
+14169D     (base 16)           Cisco Systems, Inc\r
+                               80 West Tasman Drive\r
+                               San Jose  CA  94568\r
+                               US\r
+\r
+48-A2-E6   (hex)               Resideo\r
+48A2E6     (base 16)           Resideo\r
+                               2 Corporate Center Dr.\r
+                               Melville  NY  11747\r
+                               US\r
+\r
 90-E2-FC   (hex)               IEEE Registration Authority\r
 90E2FC     (base 16)           IEEE Registration Authority\r
                                445 Hoes Lane\r
                                Piscataway  NJ  08554\r
                                US\r
 \r
+F0-08-D1   (hex)               Espressif Inc.\r
+F008D1     (base 16)           Espressif Inc.\r
+                               Room 204, Building 2, 690 Bibo Rd, Pudong New Area\r
+                               Shanghai  Shanghai  201203\r
+                               CN\r
+\r
+58-94-B2   (hex)               BrainCo\r
+5894B2     (base 16)           BrainCo\r
+                               ????????????????1107?\r
+                               ???  ???  518000\r
+                               CN\r
+\r
+B0-95-75   (hex)               TP-LINK TECHNOLOGIES CO.,LTD.\r
+B09575     (base 16)           TP-LINK TECHNOLOGIES CO.,LTD.\r
+                               Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan\r
+                               Shenzhen  Guangdong  518057\r
+                               CN\r
+\r
+B4-B0-55   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+B4B055     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+04-8C-16   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+048C16     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+98-DD-5B   (hex)               TAKUMI JAPAN LTD\r
+98DD5B     (base 16)           TAKUMI JAPAN LTD\r
+                               3-9-3 Uchiyama building 7F Nishishinbashi\r
+                               Minato-ku Tokyo  Tokyo  1050003\r
+                               JP\r
+\r
+3C-5C-F1   (hex)               eero inc.\r
+3C5CF1     (base 16)           eero inc.\r
+                               660 3rd Street\r
+                               San Francisco  CA  94107\r
+                               US\r
+\r
+14-AE-85   (hex)               IEEE Registration Authority\r
+14AE85     (base 16)           IEEE Registration Authority\r
+                               445 Hoes Lane\r
+                               Piscataway  NJ  08554\r
+                               US\r
+\r
+90-74-9D   (hex)               IRay Technology Co., Ltd.\r
+90749D     (base 16)           IRay Technology Co., Ltd.\r
+                               11th Guiyang St.,\r
+                               Yantai  Shandong  264000\r
+                               CN\r
+\r
+8C-3B-32   (hex)               Microfan B.V.\r
+8C3B32     (base 16)           Microfan B.V.\r
+                               Industriestraat 23\r
+                               Horst  Limburg  5961 PH\r
+                               NL\r
+\r
+D0-D3-E0   (hex)               Aruba, a Hewlett Packard Enterprise Company\r
+D0D3E0     (base 16)           Aruba, a Hewlett Packard Enterprise Company\r
+                               3333 Scott Blvd\r
+                               Santa Clara  CA  95054\r
+                               US\r
+\r
+3C-58-C2   (hex)               Intel Corporate\r
+3C58C2     (base 16)           Intel Corporate\r
+                               Lot 8, Jalan Hi-Tech 2/3\r
+                               Kulim  Kedah  09000\r
+                               MY\r
+\r
+CC-F9-E4   (hex)               Intel Corporate\r
+CCF9E4     (base 16)           Intel Corporate\r
+                               Lot 8, Jalan Hi-Tech 2/3\r
+                               Kulim  Kedah  09000\r
+                               MY\r
+\r
+64-5C-F3   (hex)               ParanTek Inc.\r
+645CF3     (base 16)           ParanTek Inc.\r
+                               3F, 40-15 Gilju-Ro, 411 Beon-Gil\r
+                               Wonmi-Gu, Bucheon City  Gyeonggi-Do  14488\r
+                               KR\r
+\r
+B0-CC-FE   (hex)               Huawei Device Co., Ltd.\r
+B0CCFE     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+54-0D-F9   (hex)               Huawei Device Co., Ltd.\r
+540DF9     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+00-66-19   (hex)               Huawei Device Co., Ltd.\r
+006619     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+FC-39-64   (hex)               ITEL MOBILE LIMITED\r
+FC3964     (base 16)           ITEL MOBILE LIMITED\r
+                               RM B3 & B4 BLOCK B, KO FAI INDUSTRIAL BUILDING  NO.7 KO FAI ROAD, YAU TONG, KLN, H.K\r
+                               Hong Kong  KOWLOON  999077\r
+                               HK\r
+\r
+E4-5E-37   (hex)               Intel Corporate\r
+E45E37     (base 16)           Intel Corporate\r
+                               Lot 8, Jalan Hi-Tech 2/3\r
+                               Kulim  Kedah  09000\r
+                               MY\r
+\r
+14-47-2D   (hex)               GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD\r
+14472D     (base 16)           GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD\r
+                               NO.18 HAIBIN ROAD,\r
+                               DONG GUAN  GUANG DONG  523860\r
+                               CN\r
+\r
+E4-90-FD   (hex)               Apple, Inc.\r
+E490FD     (base 16)           Apple, Inc.\r
+                               1 Infinite Loop\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+84-AB-1A   (hex)               Apple, Inc.\r
+84AB1A     (base 16)           Apple, Inc.\r
+                               1 Infinite Loop\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+20-6D-31   (hex)               FIREWALLA INC\r
+206D31     (base 16)           FIREWALLA INC\r
+                               75 E. Santa Clara St. STE 600\r
+                               San Jose  CA  95113\r
+                               US\r
+\r
+D0-65-44   (hex)               Apple, Inc.\r
+D06544     (base 16)           Apple, Inc.\r
+                               1 Infinite Loop\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+18-6F-2D   (hex)               Shenzhen Sundray Technologies Company Limited\r
+186F2D     (base 16)           Shenzhen Sundray Technologies Company Limited\r
+                               5th Floor, Block A4, Nanshan ipark,NO.1001 Xue Yuan Road, Nanshan District, Shenzhen 518055, P.R. China\r
+                               Shenzhen  Guangdong  518057\r
+                               CN\r
+\r
+F8-4F-AD   (hex)               Hui Zhou Gaoshengda Technology Co.,LTD\r
+F84FAD     (base 16)           Hui Zhou Gaoshengda Technology Co.,LTD\r
+                               No.75,Zhongkai High-Tech Development District,Huizhou\r
+                               Hui Zhou  Guangdong  516006\r
+                               CN\r
+\r
+4C-0A-3D   (hex)               ADNACOM INC.\r
+4C0A3D     (base 16)           ADNACOM INC.\r
+                               200-5050 Kingsway\r
+                               Burnaby  BC  V5H 4H2\r
+                               CA\r
+\r
+3C-80-6B   (hex)               Hunan Voc Acoustics Technology Co., Ltd.\r
+3C806B     (base 16)           Hunan Voc Acoustics Technology Co., Ltd.\r
+                               State Industrialpark, Jiulong Development Zone, Yanling County\r
+                               Zhuzhou  Hunan  412500\r
+                               CN\r
+\r
+60-DE-35   (hex)               GITSN, Inc.\r
+60DE35     (base 16)           GITSN, Inc.\r
+                                #601~602, Daerung Post Tower 1, 288, Digital-ro\r
+                               Guro-gu  Seoul  08390\r
+                               KR\r
+\r
+28-31-7E   (hex)               Hongkong Nano IC Technologies Co., Ltd\r
+28317E     (base 16)           Hongkong Nano IC Technologies Co., Ltd\r
+                                Rm. 19C, Lockhart Ctr., 301-307 Lockhart Rd., Wan Chai, Hong Kong.\r
+                               Hongkong    999077\r
+                               CN\r
+\r
+A8-41-22   (hex)               China Mobile (Hangzhou) Information Technology Co.,Ltd.\r
+A84122     (base 16)           China Mobile (Hangzhou) Information Technology Co.,Ltd.\r
+                               No. 1600 Yuhangtang Road, Wuchang Street, Yuhang District\r
+                               Hangzhou  Zhejiang  310000\r
+                               CN\r
+\r
+6C-DD-BC   (hex)               Samsung Electronics Co.,Ltd\r
+6CDDBC     (base 16)           Samsung Electronics Co.,Ltd\r
+                               #94-1, Imsoo-Dong\r
+                               Gumi  Gyeongbuk  730-350\r
+                               KR\r
+\r
+CC-7F-76   (hex)               Cisco Systems, Inc\r
+CC7F76     (base 16)           Cisco Systems, Inc\r
+                               80 West Tasman Drive\r
+                               San Jose  CA  94568\r
+                               US\r
+\r
+40-BC-68   (hex)               Wuhan Funshion Online Technologies Co.,Ltd\r
+40BC68     (base 16)           Wuhan Funshion Online Technologies Co.,Ltd\r
+                               5th Floor,Financial Port Building A9,No.77 Optical Valley Avenue, East Lake High-Tech Development Zone, Wuhan\r
+                               Wuhan  CN/Hubei  430000\r
+                               CN\r
+\r
+DC-98-40   (hex)               Microsoft Corporation\r
+DC9840     (base 16)           Microsoft Corporation\r
+                               One Microsoft Way\r
+                               REDMOND    98052\r
+                               US\r
+\r
+44-76-54   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+447654     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+7C-D9-A0   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+7CD9A0     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+F0-33-E5   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+F033E5     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+B4-F1-8C   (hex)               Huawei Device Co., Ltd.\r
+B4F18C     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+B8-CE-F6   (hex)               Mellanox Technologies, Inc.\r
+B8CEF6     (base 16)           Mellanox Technologies, Inc.\r
+                               350 Oakmead Parkway, Suite 100 \r
+                               Sunnyvale  CA  94085\r
+                               US\r
+\r
+B8-02-A4   (hex)               Aeonsemi, Inc.\r
+B802A4     (base 16)           Aeonsemi, Inc.\r
+                               Cassia Court, Suite 716, 10 Market Street\r
+                               Camana Bay  Grand Cayman  KY1-9006\r
+                               KY\r
+\r
+E4-83-26   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+E48326     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+94-05-BB   (hex)               IEEE Registration Authority\r
+9405BB     (base 16)           IEEE Registration Authority\r
+                               445 Hoes Lane\r
+                               Piscataway  NJ  08554\r
+                               US\r
+\r
+8C-5F-AD   (hex)               Fiberhome Telecommunication Technologies Co.,LTD\r
+8C5FAD     (base 16)           Fiberhome Telecommunication Technologies Co.,LTD\r
+                               No.5 DongXin Road\r
+                               Wuhan  Hubei  430074\r
+                               CN\r
+\r
+AC-C2-5D   (hex)               Fiberhome Telecommunication Technologies Co.,LTD\r
+ACC25D     (base 16)           Fiberhome Telecommunication Technologies Co.,LTD\r
+                               No.5 DongXin Road\r
+                               Wuhan  Hubei  430074\r
+                               CN\r
+\r
+8C-0C-87   (hex)               Nokia\r
+8C0C87     (base 16)           Nokia\r
+                               600 March Road\r
+                               Kanata  Ontario  K2K 2E6\r
+                               CA\r
+\r
+18-69-D8   (hex)               HANGZHOU AIXIANGJI TECHNOLOGY CO., LTD\r
+1869D8     (base 16)           HANGZHOU AIXIANGJI TECHNOLOGY CO., LTD\r
+                               7 Floor, 3 Blvd., More Centre, 87 Gudun Rd., Xihu District\r
+                               Hangzhou  Zhejiang  310012\r
+                               CN\r
+\r
+C4-32-D1   (hex)               Farlink Technology Limited\r
+C432D1     (base 16)           Farlink Technology Limited\r
+                               Flat A&B,9/F,Wing Cheong Factory Building,121 King Lam Street,Cheung Sha Wan,Hong Kong.\r
+                               Hongkong    0000\r
+                               HK\r
+\r
+CC-41-8E   (hex)               MSA Innovation\r
+CC418E     (base 16)           MSA Innovation\r
+                               1100 Cranberry Woods Road\r
+                               Cranberry Township  PA  16066\r
+                               US\r
+\r
+6C-6A-77   (hex)               Intel Corporate\r
+6C6A77     (base 16)           Intel Corporate\r
+                               Lot 8, Jalan Hi-Tech 2/3\r
+                               Kulim  Kedah  09000\r
+                               MY\r
+\r
+CC-A7-C1   (hex)               Google, Inc.\r
+CCA7C1     (base 16)           Google, Inc.\r
+                               1600 Amphitheatre Parkway\r
+                               Mountain View  CA  94043\r
+                               US\r
+\r
+38-84-79   (hex)               Cisco Meraki\r
+388479     (base 16)           Cisco Meraki\r
+                               500 Terry A. Francois Blvd\r
+                               San Francisco    94158\r
+                               US\r
+\r
+7C-9E-BD   (hex)               Espressif Inc.\r
+7C9EBD     (base 16)           Espressif Inc.\r
+                               Room 204, Building 2, 690 Bibo Rd, Pudong New Area\r
+                               Shanghai  Shanghai  201203\r
+                               CN\r
+\r
+1C-02-19   (hex)               GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD\r
+1C0219     (base 16)           GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD\r
+                               NO.18 HAIBIN ROAD,\r
+                               DONG GUAN  GUANG DONG  523860\r
+                               CN\r
+\r
+C8-D7-78   (hex)               BSH Hausgeraete GmbH\r
+C8D778     (base 16)           BSH Hausgeraete GmbH\r
+                               Im Gewerbepark B10\r
+                               Regensburg    93059\r
+                               DE\r
+\r
+9C-61-1D   (hex)               Panasonic Corporation of North America\r
+9C611D     (base 16)           Panasonic Corporation of North America\r
+                               1200 Ridgeway Ave\r
+                               Rochester  NY  14615\r
+                               US\r
+\r
+C0-95-DA   (hex)               NXP India Private Limited\r
+C095DA     (base 16)           NXP India Private Limited\r
+                               1st Floor, Muttha Towers, Don Bosco Marg, Off Airport Road, Yerwada\r
+                               Pune  Maharashtra  411006\r
+                               IN\r
+\r
+B4-22-00   (hex)               Brother Industries, LTD.\r
+B42200     (base 16)           Brother Industries, LTD.\r
+                               15-1, Naeshirocho, Mizuho-ku\r
+                               NAGOYA    4678561\r
+                               JP\r
+\r
+68-49-B2   (hex)               CARLO GAVAZZI LTD\r
+6849B2     (base 16)           CARLO GAVAZZI LTD\r
+                               BLB042, Bulebel Industrial Estate \r
+                               Zejtun     ZTN 3000\r
+                               MT\r
+\r
+48-7A-FF   (hex)               ESSYS\r
+487AFF     (base 16)           ESSYS\r
+                               gaetbeol-ro\r
+                               Incheon    21999\r
+                               KR\r
+\r
+40-B6-E7   (hex)               Huawei Device Co., Ltd.\r
+40B6E7     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+D0-B4-5D   (hex)               Huawei Device Co., Ltd.\r
+D0B45D     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+88-36-CF   (hex)               Huawei Device Co., Ltd.\r
+8836CF     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+A4-C5-4E   (hex)               Huawei Device Co., Ltd.\r
+A4C54E     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+D4-BB-E6   (hex)               Huawei Device Co., Ltd.\r
+D4BBE6     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+00-9E-EE   (hex)               Positivo Tecnologia S.A.\r
+009EEE     (base 16)           Positivo Tecnologia S.A.\r
+                               João Bettega, 5200\r
+                               Curitiba  Paraná  81350-000\r
+                               BR\r
+\r
+90-B8-32   (hex)               Extreme Networks, Inc.\r
+90B832     (base 16)           Extreme Networks, Inc.\r
+                               1011 McCarthy Blvd\r
+                               Milpitas  CA  95035\r
+                               US\r
+\r
+40-2F-86   (hex)               LG Innotek\r
+402F86     (base 16)           LG Innotek\r
+                               26, Hanamsandan 5beon-ro\r
+                               Gwangju  Gwangsan-gu  506-731\r
+                               KR\r
+\r
+D4-22-CD   (hex)               Xsens Technologies B.V.\r
+D422CD     (base 16)           Xsens Technologies B.V.\r
+                               Pantheon 6-a\r
+                               Enschede    7521 PR\r
+                               NL\r
+\r
+A4-B1-C1   (hex)               Intel Corporate\r
+A4B1C1     (base 16)           Intel Corporate\r
+                               Lot 8, Jalan Hi-Tech 2/3\r
+                               Kulim  Kedah  09000\r
+                               MY\r
+\r
+C8-67-5E   (hex)               Extreme Networks, Inc.\r
+C8675E     (base 16)           Extreme Networks, Inc.\r
+                               1011 McCarthy Blvd\r
+                               Milpitas  CA  95035\r
+                               US\r
+\r
+9C-5D-12   (hex)               Extreme Networks, Inc.\r
+9C5D12     (base 16)           Extreme Networks, Inc.\r
+                               1011 McCarthy Blvd\r
+                               Milpitas  CA  95035\r
+                               US\r
+\r
+F0-9C-E9   (hex)               Extreme Networks, Inc.\r
+F09CE9     (base 16)           Extreme Networks, Inc.\r
+                               1011 McCarthy Blvd\r
+                               Milpitas  CA  95035\r
+                               US\r
+\r
+C4-13-E2   (hex)               Extreme Networks, Inc.\r
+C413E2     (base 16)           Extreme Networks, Inc.\r
+                               1011 McCarthy Blvd\r
+                               Milpitas  CA  95035\r
+                               US\r
+\r
+F4-EB-9F   (hex)               Ellu Company 2019 SL\r
+F4EB9F     (base 16)           Ellu Company 2019 SL\r
+                               Paseo de la Castellana 144 14B\r
+                               Madrid  Madrid  28046\r
+                               ES\r
+\r
+E8-98-C2   (hex)               ZETLAB Company\r
+E898C2     (base 16)           ZETLAB Company\r
+                               Savelkinsky passage, 4\r
+                               Zelenograd  Moscow  124482\r
+                               RU\r
+\r
+D4-1A-C8   (hex)               Nippon Printer Engineering\r
+D41AC8     (base 16)           Nippon Printer Engineering\r
+                               2660 Katsuyama\r
+                               Fujikawaguchiko-town  Yamanashi-Pref.  401-0310\r
+                               JP\r
+\r
+50-61-F6   (hex)               Universal Electronics, Inc.\r
+5061F6     (base 16)           Universal Electronics, Inc.\r
+                               201 E. Sandpointe Ave\r
+                               Santa Ana  CA  92707\r
+                               US\r
+\r
+00-0C-32   (hex)               Avionic Design GmbH\r
+000C32     (base 16)           Avionic Design GmbH\r
+                               Wragekamp 10\r
+                               Hamburg    22397\r
+                               DE\r
+\r
+00-0A-0D   (hex)               Amphenol\r
+000A0D     (base 16)           Amphenol\r
+                               Holzhauser Strasse 175\r
+                               Berlin    13509\r
+                               DE\r
+\r
+F4-54-20   (hex)               TELLESCOM INDUSTRIA E COMERCIO EM TELECOMUNICACAO \r
+F45420     (base 16)           TELLESCOM INDUSTRIA E COMERCIO EM TELECOMUNICACAO \r
+                               Av. Buriti, 1900 – Setor B – Distrito Industrial\r
+                               Manaus  Amazonas  69075-000\r
+                               BR\r
+\r
+4C-40-88   (hex)               SANSHIN ELECTRONICS CO.,LTD.\r
+4C4088     (base 16)           SANSHIN ELECTRONICS CO.,LTD.\r
+                               4-4-12, Shiba, Minato-ku,\r
+                               Tokyo    108-8404\r
+                               JP\r
+\r
+64-DD-E9   (hex)               Xiaomi Communications Co Ltd\r
+64DDE9     (base 16)           Xiaomi Communications Co Ltd\r
+                               The Rainbow City of China Resources\r
+                               NO.68, Qinghe Middle Street  Haidian District, Beijing  100085\r
+                               CN\r
+\r
+0C-81-7D   (hex)               EEP Elektro-Elektronik Pranjic GmbH\r
+0C817D     (base 16)           EEP Elektro-Elektronik Pranjic GmbH\r
+                               Am Luftschacht 21\r
+                               Gelsenkirchen    45886\r
+                               DE\r
+\r
+04-F5-F4   (hex)               Proxim Wireless\r
+04F5F4     (base 16)           Proxim Wireless\r
+                               2114 Ringwood Ave\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+C8-BC-E5   (hex)               Sense Things Japan INC.\r
+C8BCE5     (base 16)           Sense Things Japan INC.\r
+                               3-5-7 Kawaramachi,Chuo-ku\r
+                               Osaka    541-0048\r
+                               JP\r
+\r
+E8-B4-70   (hex)               IEEE Registration Authority\r
+E8B470     (base 16)           IEEE Registration Authority\r
+                               445 Hoes Lane\r
+                               Piscataway  NJ  08554\r
+                               US\r
+\r
+00-1B-ED   (hex)               Brocade Communications Systems LLC\r
+001BED     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+00-0C-DB   (hex)               Brocade Communications Systems LLC\r
+000CDB     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+00-04-80   (hex)               Brocade Communications Systems LLC\r
+000480     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+60-9C-9F   (hex)               Brocade Communications Systems LLC\r
+609C9F     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+00-00-88   (hex)               Brocade Communications Systems LLC\r
+000088     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+00-60-DF   (hex)               Brocade Communications Systems LLC\r
+0060DF     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+00-60-69   (hex)               Brocade Communications Systems LLC\r
+006069     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+00-05-33   (hex)               Brocade Communications Systems LLC\r
+000533     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+B0-45-02   (hex)               Huawei Device Co., Ltd.\r
+B04502     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+1C-1F-F1   (hex)               Huawei Device Co., Ltd.\r
+1C1FF1     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+B4-EF-1C   (hex)               360 AI Technology Co.Ltd\r
+B4EF1C     (base 16)           360 AI Technology Co.Ltd\r
+                               MTK Building B?No.6 Jiuxianqiao Road, Chaoyang District, Beijing, P.R.C. \r
+                               Beijing  Beijing  100015\r
+                               CN\r
+\r
+14-DE-39   (hex)               Huawei Device Co., Ltd.\r
+14DE39     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+B8-F0-09   (hex)               Espressif Inc.\r
+B8F009     (base 16)           Espressif Inc.\r
+                               Room 204, Building 2, 690 Bibo Rd, Pudong New Area\r
+                               Shanghai  Shanghai  201203\r
+                               CN\r
+\r
+FC-71-FA   (hex)               Trane Technologies\r
+FC71FA     (base 16)           Trane Technologies\r
+                               6200 Troup Hwy.\r
+                               Tyler  TX  75707\r
+                               US\r
+\r
+CC-BE-59   (hex)               Calix Inc.\r
+CCBE59     (base 16)           Calix Inc.\r
+                               2777 Orchard Pkwy\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+24-68-B0   (hex)               Samsung Electronics Co.,Ltd\r
+2468B0     (base 16)           Samsung Electronics Co.,Ltd\r
+                               #94-1, Imsoo-Dong\r
+                               Gumi  Gyeongbuk  730-350\r
+                               KR\r
+\r
+30-FC-EB   (hex)               LG Electronics (Mobile Communications)\r
+30FCEB     (base 16)           LG Electronics (Mobile Communications)\r
+                               60-39, Gasan-dong, Geumcheon-gu\r
+                               Seoul    153-801\r
+                               KR\r
+\r
+1C-13-38   (hex)               Kimball Electronics Group, LLC\r
+1C1338     (base 16)           Kimball Electronics Group, LLC\r
+                               1205 Kimball Blvd\r
+                               Jasper  IN  47546\r
+                               US\r
+\r
+EC-63-ED   (hex)               Hyundai Autoever Corp.\r
+EC63ED     (base 16)           Hyundai Autoever Corp.\r
+                               38, Teheran-ro 114-gil\r
+                               Gangnam-gu, Seoul    06176\r
+                               KR\r
+\r
+84-A3-B5   (hex)               Propulsion systems\r
+84A3B5     (base 16)           Propulsion systems\r
+                               Dooren 72\r
+                               Merchtem  Vlaams brabant  1785\r
+                               BE\r
+\r
+9C-ED-FA   (hex)               EVUlution AG\r
+9CEDFA     (base 16)           EVUlution AG\r
+                               Via da Clalt 12\r
+                               Poschiavo  GR  7742\r
+                               CH\r
+\r
 7C-8A-E1   (hex)               COMPAL INFORMATION (KUNSHAN) CO., LTD. \r
 7C8AE1     (base 16)           COMPAL INFORMATION (KUNSHAN) CO., LTD. \r
                                NO. 25, THE 3RD Street KUNSHAN EXPORT PROCESSING ZONE \r
@@ -130874,12 +133040,6 @@ FCBD67     (base 16)         Arista Networks
                                Shenzhen  Guangdong  518000\r
                                CN\r
 \r
-48-77-46   (hex)               Calix Inc.\r
-487746     (base 16)           Calix Inc.\r
-                               2777 Orchard Parkway\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
 F8-AE-27   (hex)               John Deere Electronic Solutions\r
 F8AE27     (base 16)           John Deere Electronic Solutions\r
                                1441 44th St N\r
@@ -130904,12 +133064,6 @@ C4F7D5     (base 16)         Cisco Systems, Inc
                                New Taipei City,  Taiwan  24159\r
                                TW\r
 \r
-68-6D-BC   (hex)               Hangzhou Hikvision Digital Technology Co.,Ltd.\r
-686DBC     (base 16)           Hangzhou Hikvision Digital Technology Co.,Ltd.\r
-                               No.555 Qianmo Road, Binjiang District\r
-                               Hangzhou  Zhejiang  310052\r
-                               CN\r
-\r
 10-DC-4A   (hex)               Fiberhome Telecommunication Technologies Co.,LTD\r
 10DC4A     (base 16)           Fiberhome Telecommunication Technologies Co.,LTD\r
                                No.5 DongXin Road\r
@@ -132338,12 +134492,6 @@ CC988B     (base 16)         SONY Visual Products Inc.
                                Jinan  Shandong  250101\r
                                CN\r
 \r
-78-7D-53   (hex)               Aerohive Networks Inc.\r
-787D53     (base 16)           Aerohive Networks Inc.\r
-                               1011 McCarthy Blvd\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
 E0-45-6D   (hex)               China Mobile Group Device Co.,Ltd.\r
 E0456D     (base 16)           China Mobile Group Device Co.,Ltd.\r
                                32 Xuanwumen West Street,Xicheng District\r
@@ -132698,12 +134846,6 @@ DC9088     (base 16)         HUAWEI TECHNOLOGIES CO.,LTD
                                Cambridge    CB24 9ZR\r
                                GB\r
 \r
-44-65-7F   (hex)               Calix Inc.\r
-44657F     (base 16)           Calix Inc.\r
-                               2777 Orchard Parkway\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
 40-62-EA   (hex)               China Mobile Group Device Co.,Ltd.\r
 4062EA     (base 16)           China Mobile Group Device Co.,Ltd.\r
                                32 Xuanwumen West Street,Xicheng District\r
@@ -134315,12 +136457,6 @@ D00401     (base 16)         Motorola Mobility LLC, a Lenovo Company
                                Rueil Malmaison Cedex  hauts de seine  92848\r
                                FR\r
 \r
-28-56-C1   (hex)               Harman International\r
-2856C1     (base 16)           Harman International\r
-                               15th Fl, 400 Atlantic Street\r
-                               Stamford  CT  06901\r
-                               US\r
-\r
 B4-A3-82   (hex)               Hangzhou Hikvision Digital Technology Co.,Ltd.\r
 B4A382     (base 16)           Hangzhou Hikvision Digital Technology Co.,Ltd.\r
                                No.555 Qianmo Road\r
@@ -134351,12 +136487,6 @@ A407B6     (base 16)         Samsung Electronics Co.,Ltd
                                Matsumoto-shi  Nagano-ken  399-8702\r
                                JP\r
 \r
-24-E1-24   (hex)               Xiamen Ursaconn Technology Co. , Ltd.\r
-24E124     (base 16)           Xiamen Ursaconn Technology Co. , Ltd.\r
-                               3/F, No. 46 Guanri Road, 2nd Software Park\r
-                               Xiamen  Fujian  361008\r
-                               CN\r
-\r
 8C-0F-83   (hex)               Angie Hospitality LLC\r
 8C0F83     (base 16)           Angie Hospitality LLC\r
                                12465 S Fort St, Ste 300\r
@@ -134609,12 +136739,6 @@ ECD09F     (base 16)         Xiaomi Communications Co Ltd
                                Apeldoorn    7328JK\r
                                NL\r
 \r
-78-A6-E1   (hex)               Brocade Communications Systems, Inc.\r
-78A6E1     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose    95134\r
-                               US\r
-\r
 E4-EC-10   (hex)               Nokia Corporation\r
 E4EC10     (base 16)           Nokia Corporation\r
                                Elektroniikkatie 10\r
@@ -134921,24 +137045,6 @@ A40450     (base 16)         nFore Technology Inc.
                                Taipei  Neihu District  11491\r
                                TW\r
 \r
-00-1B-17   (hex)               Palo Alto Networks\r
-001B17     (base 16)           Palo Alto Networks\r
-                               2130 Gold Street Suite 200\r
-                               Alviso  CA  95022\r
-                               US\r
-\r
-58-49-3B   (hex)               Palo Alto Networks\r
-58493B     (base 16)           Palo Alto Networks\r
-                               4401 Great America Parkway\r
-                               Santa Clara  CA  95054\r
-                               US\r
-\r
-78-6D-94   (hex)               Palo Alto Networks\r
-786D94     (base 16)           Palo Alto Networks\r
-                               4401 Great America Pkwy\r
-                               Santa Clara  CA  95054\r
-                               US\r
-\r
 FC-5A-1D   (hex)               Hitron Technologies. Inc\r
 FC5A1D     (base 16)           Hitron Technologies. Inc\r
                                No. 1-8, Lising 1st Rd. Hsinchu Science Park, Hsinchu, 300, Taiwan, R.O.C\r
@@ -135617,12 +137723,6 @@ C4B9CD     (base 16)         Cisco Systems, Inc
                                San Jose  CA  94568\r
                                US\r
 \r
-EC-4F-82   (hex)               Calix Inc.\r
-EC4F82     (base 16)           Calix Inc.\r
-                               2777 Orchard Parkway\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
 D4-61-FE   (hex)               Hangzhou H3C Technologies Co., Limited\r
 D461FE     (base 16)           Hangzhou H3C Technologies Co., Limited\r
                                466 Changhe Road, Binjiang District\r
@@ -136226,12 +138326,6 @@ C82158     (base 16)         Intel Corporate
                                Kulim  Kedah  09000\r
                                MY\r
 \r
-7C-95-B1   (hex)               Aerohive Networks Inc.\r
-7C95B1     (base 16)           Aerohive Networks Inc.\r
-                               1011 McCarthy Blvd\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
 24-20-C7   (hex)               Sagemcom Broadband SAS\r
 2420C7     (base 16)           Sagemcom Broadband SAS\r
                                250, route de l'Empereur\r
@@ -137666,12 +139760,6 @@ D0052A     (base 16)         Arcadyan Corporation
                                Hsinchu City  Hsinchu  30071\r
                                TW\r
 \r
-EC-68-81   (hex)               Palo Alto Networks\r
-EC6881     (base 16)           Palo Alto Networks\r
-                               4401 Great America Parkway\r
-                               Santa Clara  CA  95054\r
-                               US\r
-\r
 E4-50-9A   (hex)               HW Communications Ltd\r
 E4509A     (base 16)           HW Communications Ltd\r
                                Parkfield\r
@@ -139112,12 +141200,6 @@ CC6DA0     (base 16)         Roku, Inc.
                                San Diego  CA  92101\r
                                US\r
 \r
-6C-38-A1   (hex)               Ubee Interactive Co., Limited\r
-6C38A1     (base 16)           Ubee Interactive Co., Limited\r
-                               Room 1607 Dominion Centre, 43 Queen’s Road East\r
-                               Wanchai  Hong Kong  302\r
-                               HK\r
-\r
 00-17-42   (hex)               FUJITSU LIMITED\r
 001742     (base 16)           FUJITSU LIMITED\r
                                403, Kosugi-cho 1-chome, Nakahara-ku\r
@@ -141128,18 +143210,6 @@ F483CD     (base 16)         TP-LINK TECHNOLOGIES CO.,LTD.
                                Shenzhen  Guangdong  518000\r
                                CN\r
 \r
-88-94-71   (hex)               Brocade Communications Systems, Inc.\r
-889471     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
-8C-7C-FF   (hex)               Brocade Communications Systems, Inc.\r
-8C7CFF     (base 16)           Brocade Communications Systems, Inc.\r
-                               130 Holger Way\r
-                               San Jose  CA  95134\r
-                               US\r
-\r
 14-2D-27   (hex)               Hon Hai Precision Ind. Co.,Ltd.\r
 142D27     (base 16)           Hon Hai Precision Ind. Co.,Ltd.\r
                                Building D21,No.1, East Zone 1st Road\r
@@ -145229,12 +147299,6 @@ FC0647     (base 16)         Cortland Research, LLC
                                Sunnyvale  CA  94089\r
                                US\r
 \r
-70-76-DD   (hex)               Oxyguard International A/S\r
-7076DD     (base 16)           Oxyguard International A/S\r
-                               Blokken 59\r
-                               Birkeroed    DK-3460\r
-                               DK\r
-\r
 54-61-EA   (hex)               Zaplox AB\r
 5461EA     (base 16)           Zaplox AB\r
                                Scheelev\r
@@ -145859,12 +147923,6 @@ CC14A6     (base 16)         Yichun MyEnergy Domain, Inc
                                Santa Clara  CA  95054\r
                                US\r
 \r
-64-7C-34   (hex)               Ubee Interactive Co., Limited\r
-647C34     (base 16)           Ubee Interactive Co., Limited\r
-                               Room 1607 Dominion Centre, 43 Queen’s Road East\r
-                               Wanchai  Hong Kong  302\r
-                               HK\r
-\r
 C0-A3-64   (hex)               3D Systems Massachusetts\r
 C0A364     (base 16)           3D Systems Massachusetts\r
                                19 Connector Road\r
@@ -151397,12 +153455,6 @@ EC6C9F     (base 16)         Chengdu Volans Technology CO.,LTD
                                Shindian  Taipei  231\r
                                TW\r
 \r
-00-1B-85   (hex)               MAN Diesel SE\r
-001B85     (base 16)           MAN Diesel SE\r
-                               Teglholmsgade 41\r
-                               Copenhagen    2450\r
-                               DK\r
-\r
 00-1B-89   (hex)               EMZA Visual Sense Ltd.\r
 001B89     (base 16)           EMZA Visual Sense Ltd.\r
                                20 Ha'ta'as St., Beith Hapamon\r
@@ -154370,12 +156422,6 @@ EC6C9F     (base 16)         Chengdu Volans Technology CO.,LTD
                                Seoul    135-010\r
                                KR\r
 \r
-00-0E-F3   (hex)               Smarthome\r
-000EF3     (base 16)           Smarthome\r
-                               16542 Millikan Ave.\r
-                               Irvine  CA  92606\r
-                               US\r
-\r
 00-0E-F2   (hex)               Infinico Corporation\r
 000EF2     (base 16)           Infinico Corporation\r
                                4F, F-1 Bldg., 1-2-12,\r
@@ -159464,12 +161510,6 @@ EC6C9F     (base 16)         Chengdu Volans Technology CO.,LTD
                                LENEXA  KS  66215-1239\r
                                US\r
 \r
-00-10-7F   (hex)               CRESTRON ELECTRONICS, INC.\r
-00107F     (base 16)           CRESTRON ELECTRONICS, INC.\r
-                               101 BROADWAY\r
-                               CRESSKILL  NJ  07626\r
-                               US\r
-\r
 00-10-E2   (hex)               ArrayComm, Inc.\r
 0010E2     (base 16)           ArrayComm, Inc.\r
                                3141 ZANKER ROAD\r
@@ -162101,12 +164141,6 @@ CC32E5     (base 16)         TP-LINK TECHNOLOGIES CO.,LTD.
                                Guangzhou  Guangdong  511450\r
                                CN\r
 \r
-58-59-C2   (hex)               Aerohive Networks Inc.\r
-5859C2     (base 16)           Aerohive Networks Inc.\r
-                               1011 McCarthy Blvd\r
-                               Milpitas  CA  95035\r
-                               US\r
-\r
 14-59-C3   (hex)               Creative Chips GmbH\r
 1459C3     (base 16)           Creative Chips GmbH\r
                                Im Bubenstück 1\r
@@ -162875,12 +164909,54 @@ A8705D     (base 16)                ARRIS Group, Inc.
                                Shenzhen  Guangdong  518055\r
                                CN\r
 \r
-F0-46-3B   (hex)               Comcast Cable Corporation\r
-F0463B     (base 16)           Comcast Cable Corporation\r
-                               1800 Arch Street\r
-                               Philadelphia  PA  19103\r
+1C-05-B7   (hex)               Chongqing Trantor Technology Co., Ltd.\r
+1C05B7     (base 16)           Chongqing Trantor Technology Co., Ltd.\r
+                               No.69,Huoju Avenue,Jiulongpo District.\r
+                               Chongqing  Chongqing  400050\r
+                               CN\r
+\r
+00-0E-F3   (hex)               Smartlabs, Inc. \r
+000EF3     (base 16)           Smartlabs, Inc. \r
+                               1621 Alton Parkway, Suite 100\r
+                               Irvine  CA  92606\r
+                               US\r
+\r
+A0-43-B0   (hex)               Hangzhou BroadLink Technology Co.,Ltd\r
+A043B0     (base 16)           Hangzhou BroadLink Technology Co.,Ltd\r
+                               Room 101,1/F,Unit C,Building 1,No.57 Jiang'er Road,Changhe Street,Binjiang District,Hangzhou,Zhejiang,P.R.China\r
+                               Hangzhou  Zhejiang  310052\r
+                               CN\r
+\r
+74-AC-B9   (hex)               Ubiquiti Networks Inc.\r
+74ACB9     (base 16)           Ubiquiti Networks Inc.\r
+                               2580 Orchard Pkwy\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+F4-92-BF   (hex)               Ubiquiti Networks Inc.\r
+F492BF     (base 16)           Ubiquiti Networks Inc.\r
+                               2580 Orchard Pkwy\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+D8-C5-61   (hex)               CommFront Communications Pte Ltd\r
+D8C561     (base 16)           CommFront Communications Pte Ltd\r
+                               No. 1 Yishun Industrial ST 1, #05-31 A'Posh BizHub\r
+                               SG  SG  768160\r
+                               SG\r
+\r
+0C-29-EF   (hex)               Dell Inc.\r
+0C29EF     (base 16)           Dell Inc.\r
+                               One Dell Way\r
+                               Round Rock  TX  78682\r
                                US\r
 \r
+60-D8-9C   (hex)               HMD Global Oy\r
+60D89C     (base 16)           HMD Global Oy\r
+                               Bertel Jungin aukio 9\r
+                               Espoo    02600\r
+                               FI\r
+\r
 F8-2E-8E   (hex)               Nanjing Kechen Electric Co., Ltd.\r
 F82E8E     (base 16)           Nanjing Kechen Electric Co., Ltd.\r
                                Room 202, Building 12, No. 50 Daguang road\r
@@ -162892,3 +164968,603 @@ B4C9B9     (base 16)                Sichuan AI-Link Technology Co., Ltd.
                                Anzhou, Industrial Park\r
                                Mianyang  Sichuan  622650\r
                                CN\r
+\r
+F0-46-3B   (hex)               Comcast Cable Corporation\r
+F0463B     (base 16)           Comcast Cable Corporation\r
+                               1800 Arch Street\r
+                               Philadelphia  PA  19103\r
+                               US\r
+\r
+68-D7-9A   (hex)               Ubiquiti Networks Inc.\r
+68D79A     (base 16)           Ubiquiti Networks Inc.\r
+                               2580 Orchard Pkwy\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+1C-63-BF   (hex)               SHENZHEN BROADTEL  TELECOM CO.,LTD\r
+1C63BF     (base 16)           SHENZHEN BROADTEL  TELECOM CO.,LTD\r
+                               No.14-1, Tongqing Road, Baolong street, Longgang District\r
+                               ShenZhen  GuangDong  518116\r
+                               CN\r
+\r
+AC-36-51   (hex)               Jiangsu Hengtong Terahertz Technology Co., Ltd.\r
+AC3651     (base 16)           Jiangsu Hengtong Terahertz Technology Co., Ltd.\r
+                               Room 1312, Beiyou Technology Building, Haidian District\r
+                               Beijing  Beijing  100876\r
+                               CN\r
+\r
+68-4A-76   (hex)               eero inc.\r
+684A76     (base 16)           eero inc.\r
+                               660 3rd Street\r
+                               San Francisco  CA  94107\r
+                               US\r
+\r
+68-8F-C9   (hex)               Zhuolian (Shenzhen) Communication Co., Ltd\r
+688FC9     (base 16)           Zhuolian (Shenzhen) Communication Co., Ltd\r
+                               Shengli electromechanical Co., Ltd. 201, No.19, Xixiang section, Guangshen Road, Jingbei community, Xixiang street, Bao'an District\r
+                               Shenzhen  Shenzhen  518101\r
+                               CN\r
+\r
+F0-81-75   (hex)               Sagemcom Broadband SAS\r
+F08175     (base 16)           Sagemcom Broadband SAS\r
+                               250, route de l'Empereur\r
+                               Rueil Malmaison Cedex  hauts de seine  92848\r
+                               FR\r
+\r
+D8-47-32   (hex)               TP-LINK TECHNOLOGIES CO.,LTD.\r
+D84732     (base 16)           TP-LINK TECHNOLOGIES CO.,LTD.\r
+                               Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan\r
+                               Shenzhen  Guangdong  518057\r
+                               CN\r
+\r
+28-64-B0   (hex)               Huawei Device Co., Ltd.\r
+2864B0     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+04-F1-69   (hex)               Huawei Device Co., Ltd.\r
+04F169     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+50-21-EC   (hex)               Huawei Device Co., Ltd.\r
+5021EC     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+8C-68-3A   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+8C683A     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+B4-6E-08   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+B46E08     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+00-5E-0C   (hex)               HMD Global Oy\r
+005E0C     (base 16)           HMD Global Oy\r
+                               Bertel Jungin aukio 9\r
+                               Espoo    02600\r
+                               FI\r
+\r
+B4-81-07   (hex)               SHENZHEN CHUANGWEI-RGB ELECTRONICS CO.,LTD\r
+B48107     (base 16)           SHENZHEN CHUANGWEI-RGB ELECTRONICS CO.,LTD\r
+                               Unit East Block22-24/F,Skyworth semiconductor design  Bldg., Gaoxin Ave.4.S.,Nanshan District,Shenzhen,China\r
+                               SHENZHEN  GUANGDONG  518057\r
+                               CN\r
+\r
+70-66-55   (hex)               AzureWave Technology Inc.\r
+706655     (base 16)           AzureWave Technology Inc.\r
+                               8F., No. 94, Baozhong Rd.\r
+                               New Taipei City  Taiwan  231\r
+                               TW\r
+\r
+C8-58-C0   (hex)               Intel Corporate\r
+C858C0     (base 16)           Intel Corporate\r
+                               Lot 8, Jalan Hi-Tech 2/3\r
+                               Kulim  Kedah  09000\r
+                               MY\r
+\r
+64-7C-34   (hex)               Ubee Interactive Co., Limited\r
+647C34     (base 16)           Ubee Interactive Co., Limited\r
+                               Flat/RM 1202, 12/F, AT Tower \r
+                               North Point  Hong Kong  180\r
+                               HK\r
+\r
+6C-38-A1   (hex)               Ubee Interactive Co., Limited\r
+6C38A1     (base 16)           Ubee Interactive Co., Limited\r
+                               Flat/RM 1202, 12/F, AT Tower \r
+                               North Point  Hong Kong  180\r
+                               HK\r
+\r
+78-53-0D   (hex)               Shenzhen Skyworth  Digital  Technology  CO., Ltd\r
+78530D     (base 16)           Shenzhen Skyworth  Digital  Technology  CO., Ltd\r
+                               4F,Block A, Skyworth?Building,\r
+                               Shenzhen  Guangdong  518057\r
+                               CN\r
+\r
+0C-48-C6   (hex)               CELESTICA INC.\r
+0C48C6     (base 16)           CELESTICA INC.\r
+                               1900-5140 Yonge Street PO Box 42   \r
+                               Toronto  Ontario  M2N 6L7\r
+                               CA\r
+\r
+A4-29-85   (hex)               Sichuan AI-Link Technology Co., Ltd.\r
+A42985     (base 16)           Sichuan AI-Link Technology Co., Ltd.\r
+                               Anzhou, Industrial Park\r
+                               Mianyang  Sichuan  622650\r
+                               CN\r
+\r
+78-AC-44   (hex)               Dell Inc.\r
+78AC44     (base 16)           Dell Inc.\r
+                               One Dell Way\r
+                               Round Rock  TX  78682\r
+                               US\r
+\r
+98-C8-B8   (hex)               vivo Mobile Communication Co., Ltd.\r
+98C8B8     (base 16)           vivo Mobile Communication Co., Ltd.\r
+                               #283,BBK Road\r
+                               Wusha,Chang'An  DongGuan City,Guangdong,  523860\r
+                               CN\r
+\r
+B8-D4-E7   (hex)               Aruba, a Hewlett Packard Enterprise Company\r
+B8D4E7     (base 16)           Aruba, a Hewlett Packard Enterprise Company\r
+                               3333 Scott Blvd\r
+                               Santa Clara  CA  95054\r
+                               US\r
+\r
+D8-4C-90   (hex)               Apple, Inc.\r
+D84C90     (base 16)           Apple, Inc.\r
+                               1 Infinite Loop\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+24-D0-DF   (hex)               Apple, Inc.\r
+24D0DF     (base 16)           Apple, Inc.\r
+                               1 Infinite Loop\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+6C-4A-85   (hex)               Apple, Inc.\r
+6C4A85     (base 16)           Apple, Inc.\r
+                               1 Infinite Loop\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+28-F0-33   (hex)               Apple, Inc.\r
+28F033     (base 16)           Apple, Inc.\r
+                               1 Infinite Loop\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+20-9E-F7   (hex)               Extreme Networks, Inc.\r
+209EF7     (base 16)           Extreme Networks, Inc.\r
+                               6480 Via Del Oro\r
+                               San Jose  CA  95119\r
+                               US\r
+\r
+BC-09-63   (hex)               Apple, Inc.\r
+BC0963     (base 16)           Apple, Inc.\r
+                               1 Infinite Loop\r
+                               Cupertino  CA  95014\r
+                               US\r
+\r
+18-58-69   (hex)               Sailer Electronic Co., Ltd\r
+185869     (base 16)           Sailer Electronic Co., Ltd\r
+                               No. 6, Sanxi Road, Ximagou Industrial Park, Jianxi District\r
+                               Luoyang  Henan  471000\r
+                               CN\r
+\r
+BC-2D-EF   (hex)               Realme Chongqing Mobile Telecommunications Corp.,Ltd.\r
+BC2DEF     (base 16)           Realme Chongqing Mobile Telecommunications Corp.,Ltd.\r
+                               No.178 Yulong Avenue, Yufengshan, Yubei District, Chongqing.\r
+                               Chongqing   China  401120\r
+                               CN\r
+\r
+78-81-CE   (hex)               China Mobile Iot Limited company\r
+7881CE     (base 16)           China Mobile Iot Limited company\r
+                               No. 8 Yangliu North Road, Yubei District, Chongqing, China\r
+                               Chong Qing  Chong Qing  401120\r
+                               CN\r
+\r
+BC-FF-21   (hex)               Smart Code(shenzhen)Technology Co.,Ltd\r
+BCFF21     (base 16)           Smart Code(shenzhen)Technology Co.,Ltd\r
+                               Room 1206, Satellite Building,2002 Keyuan Road, Nanshan \r
+                               Shenzhen   Guangdong (Province)   518000\r
+                               CN\r
+\r
+44-5C-E9   (hex)               Samsung Electronics Co.,Ltd\r
+445CE9     (base 16)           Samsung Electronics Co.,Ltd\r
+                               129, Samsung-ro, Youngtongl-Gu\r
+                               Suwon  Gyeonggi-Do  16677\r
+                               KR\r
+\r
+C0-16-92   (hex)               China Mobile Group Device Co.,Ltd.\r
+C01692     (base 16)           China Mobile Group Device Co.,Ltd.\r
+                               32 Xuanwumen West Street,Xicheng District\r
+                               Beijing    100053\r
+                               CN\r
+\r
+38-17-30   (hex)               Ulrich Lippert GmbH & Co KG\r
+381730     (base 16)           Ulrich Lippert GmbH & Co KG\r
+                               Christian-Henkel-Str. 12\r
+                               Berlin    12349\r
+                               DE\r
+\r
+40-2E-71   (hex)               Texas Instruments\r
+402E71     (base 16)           Texas Instruments\r
+                               12500 TI Blvd\r
+                               Dallas  TX  75243\r
+                               US\r
+\r
+70-76-DD   (hex)               OxyGuard Internation A/S\r
+7076DD     (base 16)           OxyGuard Internation A/S\r
+                               Farum Gydevej 64\r
+                               Farum    DK-3520\r
+                               DK\r
+\r
+94-E9-EE   (hex)               Huawei Device Co., Ltd.\r
+94E9EE     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+28-E3-4E   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+28E34E     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+AC-12-03   (hex)               Intel Corporate\r
+AC1203     (base 16)           Intel Corporate\r
+                               Lot 8, Jalan Hi-Tech 2/3\r
+                               Kulim  Kedah  09000\r
+                               MY\r
+\r
+64-BC-58   (hex)               Intel Corporate\r
+64BC58     (base 16)           Intel Corporate\r
+                               Lot 8, Jalan Hi-Tech 2/3\r
+                               Kulim  Kedah  09000\r
+                               MY\r
+\r
+D4-52-EE   (hex)               BSkyB Ltd\r
+D452EE     (base 16)           BSkyB Ltd\r
+                               130 Kings Road\r
+                               Brentwood  Essex  08854\r
+                               GB\r
+\r
+E0-23-FF   (hex)               Fortinet, Inc.\r
+E023FF     (base 16)           Fortinet, Inc.\r
+                               899 Kifer Road\r
+                               Sunnyvale    94086\r
+                               US\r
+\r
+8C-59-DC   (hex)               ASR Microelectronics (Shanghai) Co., Ltd.\r
+8C59DC     (base 16)           ASR Microelectronics (Shanghai) Co., Ltd.\r
+                               Building 2, NO.399 Keyuan Road,Pudong District\r
+                               Shanghai  Shanghai  201210\r
+                               CN\r
+\r
+18-82-8C   (hex)               Arcadyan Corporation\r
+18828C     (base 16)           Arcadyan Corporation\r
+                               No.8, Sec.2, Guangfu Rd.\r
+                               Hsinchu City  Hsinchu  30071\r
+                               TW\r
+\r
+9C-F0-29   (hex)               Integrated Device Technology (Malaysia) Sdn. Bhd.\r
+9CF029     (base 16)           Integrated Device Technology (Malaysia) Sdn. Bhd.\r
+                               Phase 3, Bayan Lepas FIZ\r
+                               Bayan Lepas  Penang  11900\r
+                               MY\r
+\r
+28-56-C1   (hex)               Harman/Becker Automotive Systems GmbH\r
+2856C1     (base 16)           Harman/Becker Automotive Systems GmbH\r
+                               15th Fl, 400 Atlantic Street\r
+                               Stamford  CT  06901\r
+                               US\r
+\r
+78-B8-D6   (hex)               Zebra Technologies Inc.\r
+78B8D6     (base 16)           Zebra Technologies Inc.\r
+                               ONE ZEBRA PLAZA\r
+                               HOLTSVILLE  NY  11742\r
+                               US\r
+\r
+BC-4A-56   (hex)               Cisco Systems, Inc\r
+BC4A56     (base 16)           Cisco Systems, Inc\r
+                               80 West Tasman Drive\r
+                               San Jose  CA  94568\r
+                               US\r
+\r
+6C-61-F4   (hex)               SFR\r
+6C61F4     (base 16)           SFR\r
+                               12 rue jean-philippe Rameau CS 80001\r
+                               La plaine saint denis   FRANCE  93634\r
+                               FR\r
+\r
+F4-90-CB   (hex)               IEEE Registration Authority\r
+F490CB     (base 16)           IEEE Registration Authority\r
+                               445 Hoes Lane\r
+                               Piscataway  NJ  08554\r
+                               US\r
+\r
+00-10-7F   (hex)               CRESTRON ELECTRONICS, INC.\r
+00107F     (base 16)           CRESTRON ELECTRONICS, INC.\r
+                               15 Volvo Drive\r
+                               Rockleigh  NJ  07647\r
+                               US\r
+\r
+00-1B-85   (hex)               MAN Energy Solutions\r
+001B85     (base 16)           MAN Energy Solutions\r
+                               Teglholmsgade 41\r
+                               Copenhagen    2450\r
+                               DK\r
+\r
+58-49-3B   (hex)               Palo Alto Networks\r
+58493B     (base 16)           Palo Alto Networks\r
+                               3000 Tannery Way\r
+                               Santa Clara  CA  95054\r
+                               US\r
+\r
+00-1B-17   (hex)               Palo Alto Networks\r
+001B17     (base 16)           Palo Alto Networks\r
+                               3000 Tannery Way\r
+                               Santa Clara  CA  95054\r
+                               US\r
+\r
+58-59-C2   (hex)               Extreme Networks, Inc.\r
+5859C2     (base 16)           Extreme Networks, Inc.\r
+                               1011 McCarthy Blvd\r
+                               Milpitas  CA  95035\r
+                               US\r
+\r
+B8-2F-CB   (hex)               CMS Electracom\r
+B82FCB     (base 16)           CMS Electracom\r
+                               24 Binney Road\r
+                               Kings Park  NSW  2148\r
+                               AU\r
+\r
+10-CE-45   (hex)               Miromico AG\r
+10CE45     (base 16)           Miromico AG\r
+                               Gallusstrasse 4\r
+                               Zurich  Zurich  CH-8006\r
+                               CH\r
+\r
+78-7D-53   (hex)               Extreme Networks, Inc.\r
+787D53     (base 16)           Extreme Networks, Inc.\r
+                               1011 McCarthy Blvd\r
+                               Milpitas  CA  95035\r
+                               US\r
+\r
+7C-95-B1   (hex)               Extreme Networks, Inc.\r
+7C95B1     (base 16)           Extreme Networks, Inc.\r
+                               1011 McCarthy Blvd\r
+                               Milpitas  CA  95035\r
+                               US\r
+\r
+EC-68-81   (hex)               Palo Alto Networks\r
+EC6881     (base 16)           Palo Alto Networks\r
+                               3000 Tannery Way\r
+                               Santa Clara  CA  95054\r
+                               US\r
+\r
+78-6D-94   (hex)               Palo Alto Networks\r
+786D94     (base 16)           Palo Alto Networks\r
+                               3000 Tannery Way\r
+                               Santa Clara  CA  95054\r
+                               US\r
+\r
+F8-AF-05   (hex)               Huawei Device Co., Ltd.\r
+F8AF05     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+4C-FC-AA   (hex)               Tesla,Inc.\r
+4CFCAA     (base 16)           Tesla,Inc.\r
+                               3500 Deer Creek Rd.\r
+                               PALO ALTO  CA  94304\r
+                               US\r
+\r
+CC-AB-2C   (hex)               HUMAX Co., Ltd.\r
+CCAB2C     (base 16)           HUMAX Co., Ltd.\r
+                               HUMAX Village, 216, Hwangsaeul-ro, Bu\r
+                               Seongnam-si  Gyeonggi-do  463-875\r
+                               KR\r
+\r
+6C-6D-09   (hex)               Kyowa Electronics Co.,Ltd.\r
+6C6D09     (base 16)           Kyowa Electronics Co.,Ltd.\r
+                               4-3-31 Takatsukasa\r
+                               Takarazuka  Hyogo  665-0051\r
+                               JP\r
+\r
+24-E1-24   (hex)                Xiamen Ursalink Technology Co., Ltd.\r
+24E124     (base 16)            Xiamen Ursalink Technology Co., Ltd.\r
+                               4/F, No. 63-2 Wanghai Road, 2nd Software Park\r
+                               Xiamen  Fujian  361008\r
+                               CN\r
+\r
+24-43-E2   (hex)               DASAN Network Solutions\r
+2443E2     (base 16)           DASAN Network Solutions\r
+                               DASAN Tower 8F, 49 Daewangpangyo-ro644beon-gil Bundang-gu\r
+                               Seongnam-si  Gyeonggi-do  13493\r
+                               KR\r
+\r
+A8-6A-BB   (hex)               Sagemcom Broadband SAS\r
+A86ABB     (base 16)           Sagemcom Broadband SAS\r
+                               250, route de l'Empereur\r
+                               Rueil Malmaison Cedex  hauts de seine  92848\r
+                               FR\r
+\r
+90-17-3F   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+90173F     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+54-0E-2D   (hex)               vivo Mobile Communication Co., Ltd.\r
+540E2D     (base 16)           vivo Mobile Communication Co., Ltd.\r
+                               #283,BBK Road\r
+                               Wusha,Chang'An  DongGuan City,Guangdong,  523860\r
+                               CN\r
+\r
+70-8F-47   (hex)               vivo Mobile Communication Co., Ltd.\r
+708F47     (base 16)           vivo Mobile Communication Co., Ltd.\r
+                               #283,BBK Road\r
+                               Wusha,Chang'An  DongGuan City,Guangdong,  523860\r
+                               CN\r
+\r
+A0-FF-70   (hex)               Technicolor CH USA Inc.\r
+A0FF70     (base 16)           Technicolor CH USA Inc.\r
+                               5030 Sugarloaf Parkway Bldg 6 \r
+                               Lawrenceville  GA  30044\r
+                               US\r
+\r
+60-7E-CD   (hex)               HUAWEI TECHNOLOGIES CO.,LTD\r
+607ECD     (base 16)           HUAWEI TECHNOLOGIES CO.,LTD\r
+                               No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park\r
+                               Dongguan    523808\r
+                               CN\r
+\r
+54-8A-BA   (hex)               Cisco Systems, Inc\r
+548ABA     (base 16)           Cisco Systems, Inc\r
+                               80 West Tasman Drive\r
+                               San Jose  CA  94568\r
+                               US\r
+\r
+C8-07-39   (hex)               NAKAYO Inc\r
+C80739     (base 16)           NAKAYO Inc\r
+                               1-3-2, Soja-machi\r
+                               Maebashi-shi  Gunma  371-0853\r
+                               JP\r
+\r
+8C-7C-FF   (hex)               Brocade Communications Systems LLC\r
+8C7CFF     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+AC-3C-8E   (hex)               Flextronics Computing(Suzhou)Co.,Ltd.\r
+AC3C8E     (base 16)           Flextronics Computing(Suzhou)Co.,Ltd.\r
+                               No.1 GuanPu Road. Guoxiang street , WuZhong District,Suzhou City, Jiangsu Province. \r
+                               Suzhou        215124   \r
+                               CN\r
+\r
+40-62-34   (hex)               Telink Semiconductor (Shanghai) Co., Ltd.\r
+406234     (base 16)           Telink Semiconductor (Shanghai) Co., Ltd.\r
+                               No. 1500 Zuchongzhi Rd, Building #3\r
+                               Shanghai    201203\r
+                               CN\r
+\r
+88-94-71   (hex)               Brocade Communications Systems LLC\r
+889471     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+78-A6-E1   (hex)               Brocade Communications Systems LLC\r
+78A6E1     (base 16)           Brocade Communications Systems LLC\r
+                               1320 Ridder Park Dr\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+00-94-EC   (hex)               Huawei Device Co., Ltd.\r
+0094EC     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+74-45-2D   (hex)               Huawei Device Co., Ltd.\r
+74452D     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+A4-50-06   (hex)               SHENZHEN HUACHUANG SHIDAI TECHNOLOGYCO.,LTD\r
+A45006     (base 16)           SHENZHEN HUACHUANG SHIDAI TECHNOLOGYCO.,LTD\r
+                               longhua dalang huaronglu lianjiangongyeyuan 4-5\r
+                               shenzhen  guangdong  518000\r
+                               CN\r
+\r
+C8-71-25   (hex)               Johnson Outdoors Marine Electronics d/b/a Minnkota\r
+C87125     (base 16)           Johnson Outdoors Marine Electronics d/b/a Minnkota\r
+                               1531 E Madison Ave\r
+                               Mankato  MN  56001\r
+                               US\r
+\r
+68-6D-BC   (hex)               Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+686DBC     (base 16)           Hangzhou Hikvision Digital Technology Co.,Ltd.\r
+                               No.555 Qianmo Road\r
+                               Hangzhou  Zhejiang  310052\r
+                               CN\r
+\r
+80-CF-A2   (hex)               Huawei Device Co., Ltd.\r
+80CFA2     (base 16)           Huawei Device Co., Ltd.\r
+                               No.2 of Xincheng Road, Songshan Lake Zone\r
+                               Dongguan  Guangdong  523808\r
+                               CN\r
+\r
+08-03-42   (hex)               Palo Alto Networks\r
+080342     (base 16)           Palo Alto Networks\r
+                               3000 Tannery Way\r
+                               Santa Clara  CA  95054\r
+                               US\r
+\r
+EC-6C-B5   (hex)               zte corporation\r
+EC6CB5     (base 16)           zte corporation\r
+                               12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
+                               shenzhen  guangdong  518057\r
+                               CN\r
+\r
+C0-B1-01   (hex)               zte corporation\r
+C0B101     (base 16)           zte corporation\r
+                               12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China\r
+                               shenzhen  guangdong  518057\r
+                               CN\r
+\r
+EC-4F-82   (hex)               Calix Inc.\r
+EC4F82     (base 16)           Calix Inc.\r
+                               2777 Orchard Pkwy\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+44-65-7F   (hex)               Calix Inc.\r
+44657F     (base 16)           Calix Inc.\r
+                               2777 Orchard Pkwy\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+48-77-46   (hex)               Calix Inc.\r
+487746     (base 16)           Calix Inc.\r
+                               2777 Orchard Pkwy\r
+                               San Jose  CA  95131\r
+                               US\r
+\r
+FC-3D-A5   (hex)               Arcadyan Corporation\r
+FC3DA5     (base 16)           Arcadyan Corporation\r
+                               No.8, Sec.2, Guangfu Rd.\r
+                               Hsinchu City  Hsinchu  30071\r
+                               TW\r
+\r
+BC-33-AC   (hex)               Silicon Laboratories\r
+BC33AC     (base 16)           Silicon Laboratories\r
+                               7000 W. William Cannon Dr.\r
+                               Austin  TX  78735\r
+                               US\r
+\r
+14-01-52   (hex)               Samsung Electronics Co.,Ltd\r
+140152     (base 16)           Samsung Electronics Co.,Ltd\r
+                               #94-1, Imsoo-Dong\r
+                               Gumi  Gyeongbuk  730-350\r
+                               KR\r
+\r
+94-FB-A7   (hex)               IEEE Registration Authority\r
+94FBA7     (base 16)           IEEE Registration Authority\r
+                               445 Hoes Lane\r
+                               Piscataway  NJ  08554\r
+                               US\r
index f389a3dd65dbce96c69b8cc0e5ff3b44af057297..319fc7d8de9bf27c33f034d8a0beb92824a68cee 100644 (file)
@@ -1133,12 +1133,6 @@ A4-DA-22   (hex)         DURATECH Enterprise,LLC
                                Seoul    08501\r
                                KR\r
 \r
-A4-DA-22   (hex)               Abetechs GmbH\r
-A00000-AFFFFF     (base 16)            Abetechs GmbH\r
-                               Niermannsweg 11\r
-                               Erkrath   North Rhine-Westphalia  40699\r
-                               DE\r
-\r
 A4-DA-22   (hex)               LORIOT AG\r
 400000-4FFFFF     (base 16)            LORIOT AG\r
                                Zuercherstrasse 68\r
@@ -1241,12 +1235,6 @@ B00000-BFFFFF     (base 16)              ThirdReality, Inc
                                SHEN ZHEN  GUANGDONG  518000\r
                                CN\r
 \r
-9C-43-1E   (hex)               Midas Technology DBA Phoenix Audio Technologies\r
-E00000-EFFFFF     (base 16)            Midas Technology DBA Phoenix Audio Technologies\r
-                               16 Goodyear #120\r
-                               Irvine  CA  92618\r
-                               US\r
-\r
 F8-B5-68   (hex)               Dongwoo Engineering Co.,Ltd\r
 300000-3FFFFF     (base 16)            Dongwoo Engineering Co.,Ltd\r
                                #311, dREC Techno 9-ro, Yuseong-gu\r
@@ -3560,12 +3548,6 @@ E00000-EFFFFF     (base 16)              Nanjing Yining Intelligent Technology Co., Ltd.
                                Nanjing  Jiangsu  210019\r
                                CN\r
 \r
-B0-B3-53   (hex)               Blake UK\r
-000000-0FFFFF     (base 16)            Blake UK\r
-                               177-187, Rutland Road\r
-                               Sheffield  --select--  S3 9PT\r
-                               GB\r
-\r
 B0-B3-53   (hex)               Beijing Geekplus Technology Co.,Ltd.\r
 C00000-CFFFFF     (base 16)            Beijing Geekplus Technology Co.,Ltd.\r
                                1st Floor, Building 1, Chaolai High-Tech industrial Part, Chaoyang District\r
@@ -3578,12 +3560,192 @@ D00000-DFFFFF     (base 16)            IPvideo Corporation
                                Bay Shore  NY  11706\r
                                US\r
 \r
+B0-B3-53   (hex)               Blake UK\r
+000000-0FFFFF     (base 16)            Blake UK\r
+                               177-187, Rutland Road\r
+                               Sheffield  --select--  S3 9PT\r
+                               GB\r
+\r
 B0-B3-53   (hex)               Zoox\r
 B00000-BFFFFF     (base 16)            Zoox\r
                                1149 Chess Drive\r
                                Foster City  CA  94404\r
                                US\r
 \r
+14-AE-85   (hex)               Qingdao iTechene Technologies Co., Ltd.\r
+200000-2FFFFF     (base 16)            Qingdao iTechene Technologies Co., Ltd.\r
+                               UnitA3-A4,Level8,Block A ,International Innovation Park,No.1Keyuanwei Rd.,Laoshan District\r
+                               Qingdao    266100\r
+                               CN\r
+\r
+14-AE-85   (hex)               Henfred Technology Co., Ltd.\r
+100000-1FFFFF     (base 16)            Henfred Technology Co., Ltd.\r
+                               3F.-7, No.77, Sec. 1, Xintai 5th Rd\r
+                               New Taipei City  Xizhi Dist  221\r
+                               TW\r
+\r
+14-AE-85   (hex)               MTA Systems\r
+A00000-AFFFFF     (base 16)            MTA Systems\r
+                               Pemstraße 2\r
+                               Mauthausen    4310\r
+                               AT\r
+\r
+64-62-66   (hex)               MiiVii Dynamics Technology CO.,LTD\r
+000000-0FFFFF     (base 16)            MiiVii Dynamics Technology CO.,LTD\r
+                               1408-1415 Tower A BUGG Building,No.18 N. Taipingzhuang Rd,haidian District\r
+                               Beijing  Beijing  100000\r
+                               CN\r
+\r
+64-62-66   (hex)               Annapurna labs\r
+100000-1FFFFF     (base 16)            Annapurna labs\r
+                               Matam Scientific Industries Center,   Building 8.2\r
+                               Mail box 15123  Haifa  3508409\r
+                               IL\r
+\r
+64-62-66   (hex)               Bühler AG\r
+500000-5FFFFF     (base 16)            Bühler AG\r
+                               Gupfenstrasse 5\r
+                               Uzwil    9240\r
+                               CH\r
+\r
+64-62-66   (hex)               Shenzhen Jie Shi Lian Industrial Co., LTD\r
+E00000-EFFFFF     (base 16)            Shenzhen Jie Shi Lian Industrial Co., LTD\r
+                               6F,C Building,Jinao Industrial Park,Juling Rd,Guanlan Town,Longhua\r
+                               Shenzhen  Guangdong  518000\r
+                               CN\r
+\r
+64-62-66   (hex)               Leontech Limited\r
+800000-8FFFFF     (base 16)            Leontech Limited\r
+                               1208 WorkingBerg Commercial Buildung, 41-47 Marble Road\r
+                               Hong Kong  Hong Kong  00000\r
+                               HK\r
+\r
+94-CC-04   (hex)               Sam Nazarko Trading Ltd\r
+600000-6FFFFF     (base 16)            Sam Nazarko Trading Ltd\r
+                               18 Watermill Way\r
+                               London  Surrey  SW19 2RD\r
+                               GB\r
+\r
+94-CC-04   (hex)               Hanzhuo Information Technology(Shanghai) Ltd.\r
+D00000-DFFFFF     (base 16)            Hanzhuo Information Technology(Shanghai) Ltd.\r
+                               Room 2085, building 2, 622 Yingyuan middle Road, Jiading Strict\r
+                               Shanghai    201200\r
+                               CN\r
+\r
+94-CC-04   (hex)               hyBee Inc.\r
+A00000-AFFFFF     (base 16)            hyBee Inc.\r
+                               #1003, Innovalley B, 253, Pangyo-ro, Bundang-gu\r
+                               Seongnam-si  Gyeonggi-do  13486\r
+                               KR\r
+\r
+94-05-BB   (hex)               iungo\r
+800000-8FFFFF     (base 16)            iungo\r
+                               Vrouwenlaan 62\r
+                               Zwolle  Overijssel  8017 HS\r
+                               NL\r
+\r
+94-CC-04   (hex)               Nanjing Yacer Communication Technology Co. Ltd.\r
+200000-2FFFFF     (base 16)            Nanjing Yacer Communication Technology Co. Ltd.\r
+                               333 Taiping South Road Jinling Yujingyuan 19nd  floor Unit K Qin Huai District\r
+                               nanjing  jiangsu  210000\r
+                               CN\r
+\r
+90-E2-FC   (hex)               Pars Ertebat Afzar Co.\r
+000000-0FFFFF     (base 16)            Pars Ertebat Afzar Co.\r
+                               1116 – Burlington Tower Business Bay\r
+                               Dubai    90072\r
+                               AE\r
+\r
+94-05-BB   (hex)               Dongguan CXWE Technology Co.,Ltd.\r
+200000-2FFFFF     (base 16)            Dongguan CXWE Technology Co.,Ltd.\r
+                               Room 805, building 1, No. 16, Keji 4th Road, Songshanhu\r
+                               Dongguan  Guangdong  523000\r
+                               CN\r
+\r
+94-05-BB   (hex)               Zimmer GmbH\r
+900000-9FFFFF     (base 16)            Zimmer GmbH\r
+                               Im Salmenkopf 5\r
+                               Rheinau  Baden-Württemberg  77866\r
+                               DE\r
+\r
+94-05-BB   (hex)               Qingdao Maotran Electronics co., ltd\r
+000000-0FFFFF     (base 16)            Qingdao Maotran Electronics co., ltd\r
+                               Room2907, Building 2 of Minghui International, No.39 of Shiling Road, Laoshan District\r
+                               Qingdao  Shandong  266000\r
+                               CN\r
+\r
+94-05-BB   (hex)               BAE Systems\r
+E00000-EFFFFF     (base 16)            BAE Systems\r
+                               21 continental boulevard\r
+                               Merrimack  NH  03054\r
+                               US\r
+\r
+F4-90-CB   (hex)               Cheetah Medical\r
+C00000-CFFFFF     (base 16)            Cheetah Medical\r
+                               2A Hashlosha st.\r
+                               Tel Aviv    6706055\r
+                               IL\r
+\r
+F4-90-CB   (hex)               A-dec Inc.\r
+B00000-BFFFFF     (base 16)            A-dec Inc.\r
+                               2601 Crestview Drive\r
+                               Newberg  OR  97132\r
+                               US\r
+\r
+C0-9B-F4   (hex)               LTD Delovoy Office\r
+600000-6FFFFF     (base 16)            LTD Delovoy Office\r
+                               Block “B”, floor 6, build 4/1, Stroiteley blvd\r
+                               Krasnogorsk    143401\r
+                               RU\r
+\r
+94-05-BB   (hex)               LTE-X, Inc\r
+700000-7FFFFF     (base 16)            LTE-X, Inc\r
+                               4F Ginza Showa Dori Building 8-14-14 Ginza\r
+                               Chuo-ku  Tokyo  104-0062\r
+                               JP\r
+\r
+F4-90-CB   (hex)               TEQ SA\r
+700000-7FFFFF     (base 16)            TEQ SA\r
+                               Via al Municipio 16\r
+                               Barbengo  Ticino  6917\r
+                               CH\r
+\r
+9C-43-1E   (hex)               Midas Technology, Inc. dba Stem Audio / Phoenix Au\r
+E00000-EFFFFF     (base 16)            Midas Technology, Inc. dba Stem Audio / Phoenix Au\r
+                               2552 White Road, Suite A\r
+                               Irvine  CA  92614\r
+                               US\r
+\r
+E8-B4-70   (hex)               YAWATA ELECTRIC INDUSTRIAL CO.,LTD.\r
+400000-4FFFFF     (base 16)            YAWATA ELECTRIC INDUSTRIAL CO.,LTD.\r
+                               1-17-1 Ohmorihigashi\r
+                               Ohta-ku  Tokyo  143-0012\r
+                               JP\r
+\r
+A4-DA-22   (hex)               Grundig\r
+A00000-AFFFFF     (base 16)            Grundig\r
+                               Steinhof 39\r
+                               Erkrath   North Rhine-Westphalia  40699\r
+                               DE\r
+\r
+E8-B4-70   (hex)               Tibit Communications\r
+700000-7FFFFF     (base 16)            Tibit Communications\r
+                               1 Willowbrook Court, Suite 150\r
+                               Petaluma  CA  94954\r
+                               US\r
+\r
+94-FB-A7   (hex)               Shanghai Hyco Genyong Technology Co., Ltd.\r
+900000-9FFFFF     (base 16)            Shanghai Hyco Genyong Technology Co., Ltd.\r
+                               Room 105, 1/F, Building B, No.999 of Huaxu Road, Qingpu District, Shanghai, China\r
+                               Shanghai    201702\r
+                               CN\r
+\r
+94-FB-A7   (hex)               Creotech Instruments S.A.\r
+D00000-DFFFFF     (base 16)            Creotech Instruments S.A.\r
+                               ul. Gen. L. Okulickiego 7/9\r
+                               Piaseczno  Mazovia  05-500\r
+                               PL\r
+\r
 4C-4B-F9   (hex)               Shenzhen dingsheng technology co., LTD\r
 400000-4FFFFF     (base 16)            Shenzhen dingsheng technology co., LTD\r
                                Floor 3, building 5, kaijeda industrial zone, no.97, huaxing road, langkou community, dalang street, longhua district\r
@@ -3866,12 +4028,6 @@ D0-C8-57   (hex)         Mobicon
                                Suwon-si  Gyeonggi-do  16521\r
                                KR\r
 \r
-D0-C8-57   (hex)               IFLYTEK CO.,LTD.\r
-D00000-DFFFFF     (base 16)            IFLYTEK CO.,LTD.\r
-                               National Intelligent Speech High-tech Industrialization Base, No. 666, Wangjiang Road West,\r
-                               Heifei  An hui  230088\r
-                               CN\r
-\r
 60-95-CE   (hex)               Synamedia\r
 C00000-CFFFFF     (base 16)            Synamedia\r
                                Luipaardstraat 12\r
@@ -6971,6 +7127,132 @@ C00000-CFFFFF     (base 16)             Shenzhen zhong ju  Fiber optical Co.Ltd
                                Zhuhai  Guangdong  519080\r
                                CN\r
 \r
+90-E2-FC   (hex)               Huddly AS\r
+900000-9FFFFF     (base 16)            Huddly AS\r
+                               Karenslyst Allé 51\r
+                               Oslo    0279\r
+                               NO\r
+\r
+90-E2-FC   (hex)               Shenzhen Dingsheng Intelligent Technology Co., Ltd\r
+B00000-BFFFFF     (base 16)            Shenzhen Dingsheng Intelligent Technology Co., Ltd\r
+                               10/F Block C, Skyworth Building, Gaoxin South 1st Rd., Hi-Tech Park, Nanshan District\r
+                               Shenzhen  Guangdong  518000\r
+                               CN\r
+\r
+14-AE-85   (hex)               Veo Technologies\r
+900000-9FFFFF     (base 16)            Veo Technologies\r
+                               Aldersrogade 6c, 4. sal\r
+                               København  Denmark  2100\r
+                               DK\r
+\r
+14-AE-85   (hex)               SHENZHEN HONOR ELECTRONIC CO.,LTD\r
+700000-7FFFFF     (base 16)            SHENZHEN HONOR ELECTRONIC CO.,LTD\r
+                               No. A Building, Xinghui Industrial Park, Gushu No. 2Rd,\r
+                               SHEN ZHEN  GUANG  DONG  518000\r
+                               CN\r
+\r
+94-CC-04   (hex)               Shenzhen Link technology Co.,Ltd\r
+300000-3FFFFF     (base 16)            Shenzhen Link technology Co.,Ltd\r
+                               901,9/F,Dahong High TechIndusryPark?NO.6-18,Xinhe Road,Xinqiao Community,Baoan District\r
+                               Shenzhen  Guangdong  518000\r
+                               CN\r
+\r
+94-CC-04   (hex)               SHENZHEN SANRAY TECHNOLOGY CO.,LTD\r
+500000-5FFFFF     (base 16)            SHENZHEN SANRAY TECHNOLOGY CO.,LTD\r
+                               1B08 2/F Folk Culture Industrial Park,Qunli Second Road, Baoan District\r
+                               Shenzhen  GuangDong  518101\r
+                               CN\r
+\r
+94-CC-04   (hex)               GOCOAX, INC\r
+100000-1FFFFF     (base 16)            GOCOAX, INC\r
+                               15902A Halliburton Rd #662 \r
+                               Hacienda Heights  CA  91745\r
+                               US\r
+\r
+94-05-BB   (hex)               Chengdu Zhongheng Network Co.,Ltd.\r
+500000-5FFFFF     (base 16)            Chengdu Zhongheng Network Co.,Ltd.\r
+                               No.898 Baicao Road, Chengdu High-tech Zone (Western District)\r
+                               Chengdu  Sichuan  611731\r
+                               CN\r
+\r
+D0-C8-57   (hex)               IFLYTEK CO.,LTD.\r
+D00000-DFFFFF     (base 16)            IFLYTEK CO.,LTD.\r
+                               National Intelligent Speech High-tech Industrialization Base, No. 666, Wangjiang Road West,\r
+                               Hefei  An hui  230088\r
+                               CN\r
+\r
+94-CC-04   (hex)               Shanxi Baixin Information Technology Co., Ltd.\r
+C00000-CFFFFF     (base 16)            Shanxi Baixin Information Technology Co., Ltd.\r
+                               Room 210-213, Room 215-217, Room 219-220, No.2, Yari Street, Taiyuan University Park, Shanxi Comprehensive Reform Demonstration Zone\r
+                               Taiyuan  Shanxi  030032\r
+                               CN\r
+\r
+94-05-BB   (hex)               ZIGPOS GmbH\r
+600000-6FFFFF     (base 16)            ZIGPOS GmbH\r
+                               Räcknitzhöhe 35a\r
+                               Dresden  Saxony  01217\r
+                               DE\r
+\r
+94-05-BB   (hex)               SolarEdge Technologies\r
+A00000-AFFFFF     (base 16)            SolarEdge Technologies\r
+                               1 Abba Eban St.\r
+                               Herzelia    46725\r
+                               IL\r
+\r
+F4-90-CB   (hex)               Airbeam Wireless Technologies Inc.\r
+600000-6FFFFF     (base 16)            Airbeam Wireless Technologies Inc.\r
+                               #125, 21320 Gordon Way\r
+                               Richmond  British Columbia  V6W 1J8\r
+                               CA\r
+\r
+C0-9B-F4   (hex)               Annapurna labs\r
+000000-0FFFFF     (base 16)            Annapurna labs\r
+                               Matam Scientific Industries Center,   Building 8.2\r
+                               Mail box 15123  Haifa  3508409\r
+                               IL\r
+\r
+C0-9B-F4   (hex)               Alcatraz AI Inc.\r
+900000-9FFFFF     (base 16)            Alcatraz AI Inc.\r
+                               1808 El Camino Real\r
+                               Redwood City  CA  94063\r
+                               US\r
+\r
+F4-90-CB   (hex)               RSAE Labs Inc\r
+E00000-EFFFFF     (base 16)            RSAE Labs Inc\r
+                               400 E 16th St\r
+                               Panama City  FL  32405\r
+                               US\r
+\r
+C0-9B-F4   (hex)               Big Dutchman International GmbH\r
+700000-7FFFFF     (base 16)            Big Dutchman International GmbH\r
+                               Auf der Lage, 2\r
+                               Vechta  Niedersachsen  49377\r
+                               DE\r
+\r
+E8-B4-70   (hex)               Anduril Industries\r
+C00000-CFFFFF     (base 16)            Anduril Industries\r
+                               2272 Michelson Dr\r
+                               Irvine  CA  92612\r
+                               US\r
+\r
+C0-9B-F4   (hex)               Continental Automotive Component Malaysia Sdn.Bhd.\r
+E00000-EFFFFF     (base 16)            Continental Automotive Component Malaysia Sdn.Bhd.\r
+                               2455, MK.1, Tingkat Perusahaan 2A,\r
+                               Prai Industrial Estate, Prai,  Penang  13600\r
+                               MY\r
+\r
+E8-B4-70   (hex)               Webfleet Solutions B.V.\r
+300000-3FFFFF     (base 16)            Webfleet Solutions B.V.\r
+                               De Ruijterkade 154\r
+                               Amsterdam    1011 AC\r
+                               NL\r
+\r
+E8-B4-70   (hex)               Medica Corporation\r
+D00000-DFFFFF     (base 16)            Medica Corporation\r
+                                5 Oak Park Drive\r
+                               Bedford  MA  01730\r
+                               US\r
+\r
 20-85-93   (hex)               UNILUMIN GROUP CO.,LTD\r
 300000-3FFFFF     (base 16)            UNILUMIN GROUP CO.,LTD\r
                                No.112 Yongfu Rd.,BaoanDistrict,\r
@@ -10406,6 +10688,162 @@ A00000-AFFFFF     (base 16)           Ledger
                                Paris    75002\r
                                FR\r
 \r
+14-AE-85   (hex)               Trimble LEM\r
+800000-8FFFFF     (base 16)            Trimble LEM\r
+                               10368 Westmoor Dr\r
+                               Westminster  CO  80021\r
+                               US\r
+\r
+14-AE-85   (hex)               Kayamatics Limited\r
+000000-0FFFFF     (base 16)            Kayamatics Limited\r
+                               Room 1209, Trend Centre, 29 Cheung Lee Street\r
+                               Chaiwan  NA  NA\r
+                               HK\r
+\r
+64-62-66   (hex)               Shenzhen C & D Electronics Co., Ltd.\r
+700000-7FFFFF     (base 16)            Shenzhen C & D Electronics Co., Ltd.\r
+                               9th FIoor, Building 9, No.1 Qingxiang road, BaoNeng Science and TechnoIogy Industrial Park, Longhua New District\r
+                               ShenZhen  GuangDong  518000\r
+                               CN\r
+\r
+64-62-66   (hex)               Signal Hound\r
+B00000-BFFFFF     (base 16)            Signal Hound\r
+                               1502 SE Commerce Ave Suite 101\r
+                               Battle Ground  WA  98604\r
+                               US\r
+\r
+64-62-66   (hex)               Chunghwa System Integration Co., Ltd.\r
+900000-9FFFFF     (base 16)            Chunghwa System Integration Co., Ltd.\r
+                               2F., No. 35, Aiguo E. Rd.,\r
+                               Taipei    106\r
+                               TW\r
+\r
+64-62-66   (hex)               Protectli\r
+200000-2FFFFF     (base 16)            Protectli\r
+                               1315 Hot Springs Way\r
+                               Vista  CA  92081\r
+                               US\r
+\r
+64-62-66   (hex)               Redstone Systems, Inc.\r
+400000-4FFFFF     (base 16)            Redstone Systems, Inc.\r
+                               24 School Street, 2nd floor\r
+                               Boston    02108\r
+                               US\r
+\r
+64-62-66   (hex)               Kobol Innovations Pte. Ltd.\r
+D00000-DFFFFF     (base 16)            Kobol Innovations Pte. Ltd.\r
+                               101 Cecil Street, #26-01/07 Tong Eng Building\r
+                               Singapore    069533\r
+                               SG\r
+\r
+94-05-BB   (hex)               AUSTAR HEARING SCIENCE AND TECHNILIGY(XIAMEN)CO.,LTD\r
+B00000-BFFFFF     (base 16)            AUSTAR HEARING SCIENCE AND TECHNILIGY(XIAMEN)CO.,LTD\r
+                               RM201,No.2Gaoqi South 12th Road,HuliDist\r
+                               XIamen  Fujian  361006\r
+                               CN\r
+\r
+94-05-BB   (hex)               LAO INDUSTRIA LTDA\r
+C00000-CFFFFF     (base 16)            LAO INDUSTRIA LTDA\r
+                               AV DR MAURO LINDENBERG MONTEIRO, 1003\r
+                               OSASCO  SÃO PAULO  06278010\r
+                               BR\r
+\r
+F4-90-CB   (hex)               Avilution\r
+500000-5FFFFF     (base 16)            Avilution\r
+                               103 Shoreline Dr\r
+                               Madison  AL  35758\r
+                               US\r
+\r
+F4-90-CB   (hex)               Fractyl Labs\r
+900000-9FFFFF     (base 16)            Fractyl Labs\r
+                               17 HARTWELL AVE\r
+                               LEXINGTON  MA  02421\r
+                               US\r
+\r
+F4-90-CB   (hex)               OmniNet\r
+400000-4FFFFF     (base 16)            OmniNet\r
+                               6410 Del Rio Rd\r
+                               Charlotte  NC  28277\r
+                               US\r
+\r
+C0-9B-F4   (hex)               JSC NPK ATRONIK\r
+400000-4FFFFF     (base 16)            JSC NPK ATRONIK\r
+                               VARSHAVSKOE SH, 118-1-P XLII K4 10\r
+                               Moscow  Moscow  117587\r
+                               RU\r
+\r
+C0-9B-F4   (hex)               Pinpark Inc.\r
+C00000-CFFFFF     (base 16)            Pinpark Inc.\r
+                               9F., No. 101, Sec. 2, Nanjing E. Rd.,, Zhongshan Dist.,\r
+                               Taipei  Taiwan  104\r
+                               TW\r
+\r
+F4-90-CB   (hex)               Epitel, Inc.\r
+000000-0FFFFF     (base 16)            Epitel, Inc.\r
+                               630 S. Stringfellow Ct., Unit #B\r
+                               Salt Lake City  UT  84111\r
+                               US\r
+\r
+C0-9B-F4   (hex)               NUCTECH COMPANY LIMITED\r
+B00000-BFFFFF     (base 16)            NUCTECH COMPANY LIMITED\r
+                                2/F Block A,Tongfang Building,Shuangqinglu,Haidian District\r
+                               Beijing  Beijing  100084\r
+                               CN\r
+\r
+C0-9B-F4   (hex)               Osprey Video, Inc\r
+300000-3FFFFF     (base 16)            Osprey Video, Inc\r
+                               1628 Valwood Parkway Suite 200\r
+                               Carrollton  TX  75006\r
+                               US\r
+\r
+C0-9B-F4   (hex)               Infiot Inc.\r
+500000-5FFFFF     (base 16)            Infiot Inc.\r
+                               75 E. Santa Clara St., Suite 600\r
+                               San Jose  CA  95113\r
+                               US\r
+\r
+E8-B4-70   (hex)               plc2 Design GmbH\r
+A00000-AFFFFF     (base 16)            plc2 Design GmbH\r
+                               Hugstmattweg 30\r
+                               Freiburg i. Br.    79112\r
+                               DE\r
+\r
+E8-B4-70   (hex)               DEHN SE + Co KG\r
+800000-8FFFFF     (base 16)            DEHN SE + Co KG\r
+                               Hans-Dehn-Straße 1\r
+                               Neumarkt  Bavaria  92318\r
+                               DE\r
+\r
+E8-B4-70   (hex)               internet domain name system beijing engineering research center ltd\r
+200000-2FFFFF     (base 16)            internet domain name system beijing engineering research center ltd\r
+                               4,4TH SOUTH STREET ZHONG GUAN CUN\r
+                               hai dian qu ,beijing  BEIJING  100190\r
+                               CN\r
+\r
+E8-B4-70   (hex)               DongGuan Ramaxel Memory Technology\r
+000000-0FFFFF     (base 16)            DongGuan Ramaxel Memory Technology\r
+                               No.32, Industrial East Road,Innovation Park, High-tech Industrial Development Zone, Songshan Lake, Dongguan City, Guangdong Province,China\r
+                               DongGuan  Guangdong  523808\r
+                               CN\r
+\r
+E8-B4-70   (hex)               Miltek Industries Pte Ltd\r
+900000-9FFFFF     (base 16)            Miltek Industries Pte Ltd\r
+                               62 Ubi Road 1 #10-03, Oxley Bizhub 2. Singapore 408734\r
+                               Singapore    408734\r
+                               SG\r
+\r
+E8-B4-70   (hex)               Elcoma\r
+600000-6FFFFF     (base 16)            Elcoma\r
+                               Rua Barbosa Lima, 149\r
+                               Recife  Pernambuco  50030-330\r
+                               BR\r
+\r
+E8-B4-70   (hex)               Alperia Fiber srl \r
+500000-5FFFFF     (base 16)            Alperia Fiber srl \r
+                               Via Dodiciville 8\r
+                               Bolzano   bz  39100\r
+                               IT\r
+\r
 4C-4B-F9   (hex)               Shandong Linkotech Electronic Co., Ltd.\r
 600000-6FFFFF     (base 16)            Shandong Linkotech Electronic Co., Ltd.\r
                                22nd Floor, Building 2, Aosheng Building, No.1166 Xinyi Street, High-tech Zone\r
@@ -11186,12 +11624,6 @@ E00000-EFFFFF     (base 16)            NC-LINK Technology Co., Ltd.
                                Shenzhen  Guangdong  518101\r
                                CN\r
 \r
-7C-BC-84   (hex)               VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD\r
-D00000-DFFFFF     (base 16)            VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD\r
-                               B-11 SECTOR-VII\r
-                               NOIDA  UTTAR PRADESH  201301\r
-                               IN\r
-\r
 7C-BC-84   (hex)               Shanghai Yitu Technology Co. Ltd\r
 300000-3FFFFF     (base 16)            Shanghai Yitu Technology Co. Ltd\r
                                23/F, Tower 1, No.523 Loushanguan Road, Changning District\r
@@ -13874,6 +14306,117 @@ D00000-DFFFFF     (base 16)           Beijing Lanxum Computer Technology CO.,LTD.
                                Beijing  Beijing  100192\r
                                CN\r
 \r
+90-E2-FC   (hex)               ShenZhen Temwey Innovation Technology Co.,Ltd.\r
+200000-2FFFFF     (base 16)            ShenZhen Temwey Innovation Technology Co.,Ltd.\r
+                               Room 1008, 10/F, Bld.B, Bantian International Centre, No. 5 South Huancheng Road, Bantian Street of Shenzhen Longgang District\r
+                               SHENZHEN  GUANGDONG  518129\r
+                               CN\r
+\r
+90-E2-FC   (hex)               Dongguan Kangyong electronics technology Co. Ltd\r
+400000-4FFFFF     (base 16)            Dongguan Kangyong electronics technology Co. Ltd\r
+                               No 9,Yincheng 1st Road, Xiabian Village, Chang’an Town\r
+                               Dongguan  GuangDong  523877\r
+                               CN\r
+\r
+90-E2-FC   (hex)               bitsensing Inc.\r
+800000-8FFFFF     (base 16)            bitsensing Inc.\r
+                               165, Yeoksam-ro,\r
+                               Gangnam-gu, Seoul,  Republic of Korea  06247\r
+                               KR\r
+\r
+90-E2-FC   (hex)               Stanley Security\r
+C00000-CFFFFF     (base 16)            Stanley Security\r
+                               8350 Sunlight Drive\r
+                               Fishers  IN  46037\r
+                               US\r
+\r
+14-AE-85   (hex)               iSolution Technologies Co.,Ltd.\r
+D00000-DFFFFF     (base 16)            iSolution Technologies Co.,Ltd.\r
+                               5F,Bldg #6, Zhongguan Honghualing Industrial South Park\r
+                               Shenzhen  Guangdong  518055\r
+                               CN\r
+\r
+7C-BC-84   (hex)               VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD\r
+D00000-DFFFFF     (base 16)            VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD\r
+                               B3, Bredon House, 321, Tettenhall Road, Tettenhall\r
+                               Wolverhampton  West Midlands  WV6 0JZ\r
+                               GB\r
+\r
+64-62-66   (hex)               FaceHeart Inc.\r
+300000-3FFFFF     (base 16)            FaceHeart Inc.\r
+                               Rm. 8, 19F., No.118, Ciyun Rd., East Dist.\r
+                               Hsinchu  Taiwan  300\r
+                               TW\r
+\r
+64-62-66   (hex)               Sensoro Co., Ltd.\r
+A00000-AFFFFF     (base 16)            Sensoro Co., Ltd.\r
+                               7F D-Block, Lei Shing Hong Center, No. 8 Guangshun South Street, Chaoyang District,\r
+                               Beijing  Beijing  100102\r
+                               CN\r
+\r
+64-62-66   (hex)               Pass & Seymour, Inc d/b/a Legrand\r
+600000-6FFFFF     (base 16)            Pass & Seymour, Inc d/b/a Legrand\r
+                               50 Boyd Ave\r
+                               Syracuse  NY  13209\r
+                               US\r
+\r
+94-CC-04   (hex)               Shandong free optical technology co., ltd.\r
+B00000-BFFFFF     (base 16)            Shandong free optical technology co., ltd.\r
+                               195 East First Street, Industrial First Street, Economic Development Zone, Weifang, Weicheng District,\r
+                               Weifeng  Shandong  216000\r
+                               CN\r
+\r
+94-CC-04   (hex)               ENTEC Electric & Electronic Co., LTD.\r
+900000-9FFFFF     (base 16)            ENTEC Electric & Electronic Co., LTD.\r
+                               78-2 Buncheon-ri, Bongdam-eup\r
+                               Hwaseong-city  Gyungki-do  445-894\r
+                               KR\r
+\r
+94-CC-04   (hex)               SynchronicIT BV\r
+E00000-EFFFFF     (base 16)            SynchronicIT BV\r
+                               Spoorstraat 155, room 413\r
+                               Gennep  Nederland  6591 GT\r
+                               NL\r
+\r
+F4-90-CB   (hex)               Simavita (Aust) Pty Ltd\r
+D00000-DFFFFF     (base 16)            Simavita (Aust) Pty Ltd\r
+                               Suite 2.02, L2, 54 Miller Street\r
+                               North Sydney  NSW  2060\r
+                               AU\r
+\r
+F4-90-CB   (hex)               Ricker Lyman Robotic\r
+300000-3FFFFF     (base 16)            Ricker Lyman Robotic\r
+                               319 Main Street\r
+                               Beacon  NY  12508\r
+                               US\r
+\r
+F4-90-CB   (hex)               Beijing Penslink Co., Ltd.\r
+800000-8FFFFF     (base 16)            Beijing Penslink Co., Ltd.\r
+                               502,12rd floor,no.2,Fangheng International Center Beijing, Chaoyang district 100102\r
+                               Beijing  Beijing  100102\r
+                               CN\r
+\r
+F4-90-CB   (hex)               Private\r
+A00000-AFFFFF     (base 16)            Private\r
+\r
+C0-9B-F4   (hex)               The Professional Monitor Company Ltd\r
+D00000-DFFFFF     (base 16)            The Professional Monitor Company Ltd\r
+                               Holme Court A1\r
+                               Biggleswade  Bedfordshire  SG189ST\r
+                               GB\r
+\r
+C0-9B-F4   (hex)               Connected Space Management\r
+100000-1FFFFF     (base 16)            Connected Space Management\r
+                               62 boulevard Diderot\r
+                               Paris    75012\r
+                               FR\r
+\r
+C0-9B-F4   (hex)               Inveo\r
+A00000-AFFFFF     (base 16)            Inveo\r
+                               Rzemieslnicza 21\r
+                               Kozy    43-340\r
+                               PL\r
+\r
 20-85-93   (hex)               Great Lite International\r
 700000-7FFFFF     (base 16)            Great Lite International\r
                                11F., No.207-2, Sec. 3, Beixin Rd., Xindian Dist.,\r
@@ -16421,12 +16964,6 @@ B0-C5-CA   (hex)               LOWOTEC GmbH
                                Oldenburg  Niedersachsen  26129\r
                                DE\r
 \r
-DC-44-27   (hex)               Tesla Motors, Inc\r
-100000-1FFFFF     (base 16)            Tesla Motors, Inc\r
-                               3500 Deer Creek Road\r
-                               Palo Alto  CA  94304\r
-                               US\r
-\r
 78-C2-C0   (hex)               ShenZhen TuLing Robot CO.,LTD\r
 500000-5FFFFF     (base 16)            ShenZhen TuLing Robot CO.,LTD\r
                                BLK 9, No 28, Langshan Road, Northern District of High Tech. Industry Park, Nanshan Dist., SZ., PRC.\r
@@ -17273,20 +17810,212 @@ B0-B3-53   (hex)             Sprocomm Technologies CO.,LTD.
                                shenzhen  guangdong  518000\r
                                CN\r
 \r
+B0-B3-53   (hex)               VOXISCOM\r
+800000-8FFFFF     (base 16)            VOXISCOM\r
+                               Rue Jules Ferry\r
+                               PORNIC    44210\r
+                               FR\r
+\r
+B0-B3-53   (hex)               Innotas Elektronik GmbH\r
+400000-4FFFFF     (base 16)            Innotas Elektronik GmbH\r
+                               Rathenaustr. 18a\r
+                               Zittau    D-02763\r
+                               DE\r
+\r
 3C-FA-D3   (hex)               Mirico\r
 E00000-EFFFFF     (base 16)            Mirico\r
                                30 DongSan Rd 9th floor Mirico\r
                                Ansan  Gyunggi  15434\r
                                KR\r
 \r
-B0-B3-53   (hex)               Innotas Elektronik GmbH\r
-400000-4FFFFF     (base 16)            Innotas Elektronik GmbH\r
-                               Rathenaustr. 18a\r
-                               Zittau    D-02763\r
+90-E2-FC   (hex)               Power Engineering & Manufacturing, Inc.\r
+A00000-AFFFFF     (base 16)            Power Engineering & Manufacturing, Inc.\r
+                               1463 94th Lane NE\r
+                               Blaine  MN  55449\r
+                               US\r
+\r
+90-E2-FC   (hex)               Sindoh Techno Co., Ltd.\r
+600000-6FFFFF     (base 16)            Sindoh Techno Co., Ltd.\r
+                               Sindoh Bldg., 6, Hyoryeong-ro 61-gil, Seocho-gu\r
+                               Seoul    06643\r
+                               KR\r
+\r
+90-E2-FC   (hex)               Shenzhen Hisource Technology Development CO.,Ltd.\r
+300000-3FFFFF     (base 16)            Shenzhen Hisource Technology Development CO.,Ltd.\r
+                               Dalang\r
+                               Shenzhen  Guangdong  518109\r
+                               CN\r
+\r
+90-E2-FC   (hex)               TOTALONE TECHNOLOGY CO., LTD.\r
+500000-5FFFFF     (base 16)            TOTALONE TECHNOLOGY CO., LTD.\r
+                               3F.-1, NO.18, LN. 48, XingShan RD.,\r
+                               Taipei  Neihu dist  11469\r
+                               TW\r
+\r
+90-E2-FC   (hex)               Yite technology\r
+100000-1FFFFF     (base 16)            Yite technology\r
+                               No. 56, Xiaobei Rd., North Dist\r
+                               tainan    70448 \r
+                               TW\r
+\r
+90-E2-FC   (hex)               DevCom spol. s r.o.\r
+E00000-EFFFFF     (base 16)            DevCom spol. s r.o.\r
+                               Božanovská 884\r
+                               Praha  Select a State  19300\r
+                               CZ\r
+\r
+14-AE-85   (hex)               IO Industries Inc.\r
+C00000-CFFFFF     (base 16)            IO Industries Inc.\r
+                               15940 Robin's Hill Rd\r
+                               London  Ontario  N5V 0A4\r
+                               CA\r
+\r
+14-AE-85   (hex)               AZ-TECHNOLOGY SDN BHD\r
+500000-5FFFFF     (base 16)            AZ-TECHNOLOGY SDN BHD\r
+                               A108 & A109 BLOCK A KELANA BUSINESS CENTRE NO: 97 JALAN SS7/2 KELANA JAYA\r
+                               PETALING JAYA  SELANGOR  47301\r
+                               MY\r
+\r
+14-AE-85   (hex)               CENTERVUE SPA\r
+400000-4FFFFF     (base 16)            CENTERVUE SPA\r
+                               VIA SAN MARCO 9/H\r
+                               PADOVA  PADOVA  35129\r
+                               IT\r
+\r
+90-E2-FC   (hex)               Fair Winds Digital srl\r
+700000-7FFFFF     (base 16)            Fair Winds Digital srl\r
+                               Via Italo Svevo 85\r
+                               Rome  Italy  00137\r
+                               IT\r
+\r
+14-AE-85   (hex)               TMG TE GmbH\r
+600000-6FFFFF     (base 16)            TMG TE GmbH\r
+                               Zur Gießerei 10\r
+                               Karlsruhe    776227\r
                                DE\r
 \r
-B0-B3-53   (hex)               VOXISCOM\r
-800000-8FFFFF     (base 16)            VOXISCOM\r
-                               Rue Jules Ferry\r
-                               PORNIC    44210\r
+14-AE-85   (hex)               NTC SOFT\r
+B00000-BFFFFF     (base 16)            NTC SOFT\r
+                               B-805, Gwangmyeong SK Techno park, 60, Haan-ro,\r
+                               Gwangmyeong-si  Gyeonggi-do  14322\r
+                               KR\r
+\r
+14-AE-85   (hex)               Sercomm Corporation.\r
+E00000-EFFFFF     (base 16)            Sercomm Corporation.\r
+                               3F,No.81,Yu-Yih Rd.,Chu-Nan Chen\r
+                               Miao-Lih Hsuan    115\r
+                               TW\r
+\r
+64-62-66   (hex)               Jiangsu Aisida Electronic Co.,Ltd\r
+C00000-CFFFFF     (base 16)            Jiangsu Aisida Electronic Co.,Ltd\r
+                               Aisida Industrial Park,Lanling Road,Danyang Development Zone\r
+                               DanYang  JiangSu  212300\r
+                               CN\r
+\r
+94-CC-04   (hex)               Hangzhou Yongkong Technology Co., Ltd.\r
+000000-0FFFFF     (base 16)            Hangzhou Yongkong Technology Co., Ltd.\r
+                               Room 503, Building 12, Lefu Zhihui Garden, 28 Xiangyuan Road, Gongshu Distric\r
+                               Hangzhou  Zhejiang  310000\r
+                               CN\r
+\r
+94-CC-04   (hex)               Gowing Business And Contracting Wenzhou Co., LTD\r
+700000-7FFFFF     (base 16)            Gowing Business And Contracting Wenzhou Co., LTD\r
+                               Room 101, No.4 Liming Industrial District, Lucheng, Wenzhou, China\r
+                               Wenzhou    325000\r
+                               CN\r
+\r
+94-CC-04   (hex)               CircuitWerkes, Inc.\r
+800000-8FFFFF     (base 16)            CircuitWerkes, Inc.\r
+                               2805 NW 6th St\r
+                               Gainesville  FL  32609\r
+                               US\r
+\r
+14-AE-85   (hex)               IFLYTEK CO.,LTD.\r
+300000-3FFFFF     (base 16)            IFLYTEK CO.,LTD.\r
+                               National Intelligent Speech High-tech Industrialization Base, No. 666, Wangjiang Road West,\r
+                               Hefei  An hui  230088\r
+                               CN\r
+\r
+94-05-BB   (hex)               Shenzhen Baolijie Technology Co., Ltd.\r
+400000-4FFFFF     (base 16)            Shenzhen Baolijie Technology Co., Ltd.\r
+                               D2,No.47,Shasan Road,Sha jing Street,Baoan District\r
+                               Shenzhen  Kowloon  518104\r
+                               CN\r
+\r
+94-CC-04   (hex)               ProConnections, Inc.\r
+400000-4FFFFF     (base 16)            ProConnections, Inc.\r
+                               30 Massachusetts, Ave, Suite 301\r
+                               North Andover  MA  01845\r
+                               US\r
+\r
+94-05-BB   (hex)               Neurik AG\r
+300000-3FFFFF     (base 16)            Neurik AG\r
+                               Im alten Riet 143\r
+                               Schaan  SCHAAN  9494\r
+                               LI\r
+\r
+94-05-BB   (hex)               Dongguan Kingtron Electronics Tech Co., Ltd\r
+100000-1FFFFF     (base 16)            Dongguan Kingtron Electronics Tech Co., Ltd\r
+                               No.3 Fumin North Rd,Shu'an Industrial Park, Humen Town\r
+                               Dongguan  Guangdong China  523929\r
+                               CN\r
+\r
+94-05-BB   (hex)               Sunthink S&T Development Co.,Ltd\r
+D00000-DFFFFF     (base 16)            Sunthink S&T Development Co.,Ltd\r
+                               A3-f1, xinghezhong Technology Green Valley, No.14, luolei Industrial Avenue, Shiyan street, Bao'an District\r
+                               Shenzhen    518100\r
+                               CN\r
+\r
+DC-44-27   (hex)               Tesla,Inc.\r
+100000-1FFFFF     (base 16)            Tesla,Inc.\r
+                               3500 Deer Creek Road\r
+                               Palo Alto  CA  94304\r
+                               US\r
+\r
+F4-90-CB   (hex)               ICE Gateway GmbH\r
+200000-2FFFFF     (base 16)            ICE Gateway GmbH\r
+                               Am Studio 2\r
+                               Berlin  Berlin  12489\r
+                               DE\r
+\r
+F4-90-CB   (hex)               DELEM BV\r
+100000-1FFFFF     (base 16)            DELEM BV\r
+                               LUCHTHAVEN WEG 42\r
+                               5657 EB EINDHOVEN    \r
+                               NL\r
+\r
+C0-9B-F4   (hex)               SHENZHEN WINS ELECTRONIC TECHNOLOGY CO., LTD\r
+800000-8FFFFF     (base 16)            SHENZHEN WINS ELECTRONIC TECHNOLOGY CO., LTD\r
+                               Baoan Xixiang Xinbaoji Industry Park,Building A1-2\r
+                               Shenzhen  Guangdong  518026\r
+                               CN\r
+\r
+C0-9B-F4   (hex)               Hitachi High-Tech Materials Corporation\r
+200000-2FFFFF     (base 16)            Hitachi High-Tech Materials Corporation\r
+                               Toranomon Hills Business Tower, 1-17-1 Toranomon, Minato-ku\r
+                               Tokyo    105-6413\r
+                               JP\r
+\r
+E8-B4-70   (hex)               Autocom Diagnostic Partner AB\r
+100000-1FFFFF     (base 16)            Autocom Diagnostic Partner AB\r
+                               Grafitvägen 23B\r
+                               TROLLHÄTTAN    46138\r
+                               SE\r
+\r
+E8-B4-70   (hex)               UNICACCES GROUPE\r
+E00000-EFFFFF     (base 16)            UNICACCES GROUPE\r
+                               24 Chemin des Vieilles Vignes\r
+                               La tour-d'aigues  Vaucluse  84240\r
                                FR\r
+\r
+E8-B4-70   (hex)               Digifocus Technology Inc.\r
+B00000-BFFFFF     (base 16)            Digifocus Technology Inc.\r
+                               6F, No.89, Xinhu 1st Rd., Neihu Dist.\r
+                               Taipei City     11494\r
+                               TW\r
+\r
+94-FB-A7   (hex)               Silver-I Co.,LTD.\r
+800000-8FFFFF     (base 16)            Silver-I Co.,LTD.\r
+                               2-14-4 Shinyokohama,kohoku-ku\r
+                               Yokohama  Kanagawa  222-0033\r
+                               JP\r
index d934b186133358206204f535da5214cdbca9d1fb..e31a6ea3803585450687ad813d0100d1f9608bbf 100644 (file)
@@ -2927,12 +2927,6 @@ D3F000-D3FFFF     (base 16)              GLOBALCOM ENGINEERING SPA
                                MORNAGO  VA  21020\r
                                IT\r
 \r
-70-B3-D5   (hex)               VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD\r
-6BE000-6BEFFF     (base 16)            VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD\r
-                               B-11 SECTOR-VII\r
-                               NOIDA  UTTAR PRADESH  201301\r
-                               IN\r
-\r
 70-B3-D5   (hex)               Precitec Optronik GmbH\r
 0C5000-0C5FFF     (base 16)            Precitec Optronik GmbH\r
                                Schleussnerstraße 54\r
@@ -4205,6 +4199,180 @@ EF1000-EF1FFF     (base 16)             Nanotok LLC
                                Hong Kong  Hong Kong  00000\r
                                HK\r
 \r
+70-B3-D5   (hex)               Technology Link Corporation\r
+B1B000-B1BFFF     (base 16)            Technology Link Corporation\r
+                               Shin-Yokohama Kohoku-ku\r
+                               yokohama  kanagawa  222-0033\r
+                               JP\r
+\r
+70-B3-D5   (hex)               VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD\r
+6BE000-6BEFFF     (base 16)            VANTAGE INTEGRATED SECURITY SOLUTIONS PVT LTD\r
+                               B3, Bredon House, 321, Tettenhall Road, Tettenhall\r
+                               Wolverhampton  West Midlands  WV6 0JZ\r
+                               GB\r
+\r
+70-B3-D5   (hex)               Todd Digital Limited\r
+C9A000-C9AFFF     (base 16)            Todd Digital Limited\r
+                               Level 15, 95 Customhouse Quay\r
+                               Wellington    6011\r
+                               NZ\r
+\r
+70-B3-D5   (hex)               JENG IoT BV\r
+5AF000-5AFFFF     (base 16)            JENG IoT BV\r
+                               Steenbokstraat 33\r
+                               APELDOORN  Gelderland  7324 AZ\r
+                               NL\r
+\r
+70-B3-D5   (hex)               TXMission Ltd.\r
+F47000-F47FFF     (base 16)            TXMission Ltd.\r
+                               CP House, Otterspool Way\r
+                               Watford  Hertfordshire  WD25 8HU\r
+                               GB\r
+\r
+70-B3-D5   (hex)               sensorway\r
+C52000-C52FFF     (base 16)            sensorway\r
+                               A-339 samsong techno valley, 140 tongilro, deockyanggu\r
+                               goyangsi  gyeonggido  10594\r
+                               KR\r
+\r
+70-B3-D5   (hex)               Tucsen Photonics Co., Ltd. \r
+8A7000-8A7FFF     (base 16)            Tucsen Photonics Co., Ltd. \r
+                               6F NO.1 building Caimao Zone, 756# Qi an Road, Gaishan Town, Cangshan Area, Fuzhou, Fujian, PR, CHINA.\r
+                               fuzhou    350000\r
+                               CN\r
+\r
+70-B3-D5   (hex)               Beijing Yourong Runda Rechnology Development Co.Ltd.\r
+980000-980FFF     (base 16)            Beijing Yourong Runda Rechnology Development Co.Ltd.\r
+                               Changping District Science and Technology Park Advanced Road 37\r
+                               Beijing    6219650\r
+                               CN\r
+\r
+70-B3-D5   (hex)               KDT Corp.\r
+E72000-E72FFF     (base 16)            KDT Corp.\r
+                               no1705-1, BLDG 3#, Lantian shixin plaza, keqiao zone\r
+                               shaoxing  zhejiang  312030\r
+                               CN\r
+\r
+70-B3-D5   (hex)               AUTOMATICA Y REGULACION S.A.\r
+EBF000-EBFFFF     (base 16)            AUTOMATICA Y REGULACION S.A.\r
+                               Condell 1735, Nunoa\r
+                               Santiago  RM  7770331\r
+                               CL\r
+\r
+70-B3-D5   (hex)               R.C. Systems Inc\r
+52F000-52FFFF     (base 16)            R.C. Systems Inc\r
+                               8621 hwy. 6\r
+                               hitchcock  TX  77563\r
+                               US\r
+\r
+70-B3-D5   (hex)               Dalcnet srl\r
+1CF000-1CFFFF     (base 16)            Dalcnet srl\r
+                               Via Meucci 35\r
+                               Brendola  Vicenza  36040\r
+                               IT\r
+\r
+70-B3-D5   (hex)               Digital Solutions JSC\r
+D9F000-D9FFFF     (base 16)            Digital Solutions JSC\r
+                               room 4, office 1, 3rd floor, building 7, house 9a, 2nd Sinichkina Str.\r
+                               Moscow    111020\r
+                               RU\r
+\r
+70-B3-D5   (hex)               DOGA\r
+62A000-62AFFF     (base 16)            DOGA\r
+                               11 rue Lavoisier\r
+                               MAUREPAS    78310\r
+                               FR\r
+\r
+70-B3-D5   (hex)               Oculii\r
+B96000-B96FFF     (base 16)            Oculii\r
+                               829 Space Dr\r
+                               Beavercreek  OH  45434\r
+                               US\r
+\r
+70-B3-D5   (hex)               XPS ELETRONICA LTDA\r
+4F3000-4F3FFF     (base 16)            XPS ELETRONICA LTDA\r
+                               AVENIDA JAÇANÃ, 470/474 - VILA NELSON\r
+                               SÃO PAULO  SÃO PAULO  02273-001\r
+                               BR\r
+\r
+70-B3-D5   (hex)               Firecom, Inc.\r
+331000-331FFF     (base 16)            Firecom, Inc.\r
+                               3927 59th Street\r
+                               Woodside  NY  11377\r
+                               US\r
+\r
+70-B3-D5   (hex)               Remote Diagnostic Technologies Ltd\r
+C99000-C99FFF     (base 16)            Remote Diagnostic Technologies Ltd\r
+                               Pavilion C2 Ashwood Park, Ashwood Way\r
+                               Basingstoke  Hampshire  RG23 8BG\r
+                               GB\r
+\r
+70-B3-D5   (hex)               NEUROPHET, Inc.\r
+E31000-E31FFF     (base 16)            NEUROPHET, Inc.\r
+                               3rd Floor, 175, Yeoksam-ro, Gangnam-gu, seoul\r
+                               Seoul  Province  06247\r
+                               KR\r
+\r
+70-B3-D5   (hex)               Chromateq\r
+944000-944FFF     (base 16)            Chromateq\r
+                               191, allée de Lauzard, Bat. B, RDC 1 (Chromateq)\r
+                               Saint Gély du Fesc    34980\r
+                               FR\r
+\r
+70-B3-D5   (hex)               Elk Solutions, LLC\r
+1A7000-1A7FFF     (base 16)            Elk Solutions, LLC\r
+                               12708 Misty Grove St\r
+                               Moorpark  CA  93021\r
+                               US\r
+\r
+70-B3-D5   (hex)               KAYA Instruments\r
+F3D000-F3DFFF     (base 16)            KAYA Instruments\r
+                               20 HaMesila St.\r
+                               Nesher     3688520\r
+                               IL\r
+\r
+70-B3-D5   (hex)               Gogo Business Aviation\r
+3E0000-3E0FFF     (base 16)            Gogo Business Aviation\r
+                               105 Edgeview Dr., Suite 300\r
+                               Broomfield  CO  80021\r
+                               US\r
+\r
+70-B3-D5   (hex)               Asiga Pty Ltd\r
+53E000-53EFFF     (base 16)            Asiga Pty Ltd\r
+                               Unit 2, 19-21 Bourke Road\r
+                               Alexandria  New South Wales  2015\r
+                               AU\r
+\r
+70-B3-D5   (hex)               ENABLER LTD.\r
+15A000-15AFFF     (base 16)            ENABLER LTD.\r
+                               29F Shiroyama Trust Tower 4-3-1 Toranomon \r
+                               Minato-ku  Tokyo  105-6029\r
+                               JP\r
+\r
+70-B3-D5   (hex)               LINEAGE POWER PVT LTD.,\r
+62E000-62EFFF     (base 16)            LINEAGE POWER PVT LTD.,\r
+                               30-A1, KIADB, 1ST PHASE INDUSTRIAL ESTATE,KUMBALGODU, BANGALORE-MYSORE ROAD\r
+                               BANGALORE  KARNATAKA  560074\r
+                               IN\r
+\r
+70-B3-D5   (hex)               Salupo Sas\r
+898000-898FFF     (base 16)            Salupo Sas\r
+                               Via Laganeto n. 129\r
+                               Rocca di Capri Leone  Italia / ME / Sicilia  98070\r
+                               IT\r
+\r
+70-B3-D5   (hex)               Nippon Safety co,ltd\r
+872000-872FFF     (base 16)            Nippon Safety co,ltd\r
+                               1, suimeicho\r
+                               Amagasaki  Hyogo  660-0082\r
+                               JP\r
+\r
+70-B3-D5   (hex)               Grupo Epelsa S.L.\r
+40D000-40DFFF     (base 16)            Grupo Epelsa S.L.\r
+                               C/ Punto Net,3\r
+                               Alcala de Henares  Madrid  28805\r
+                               ES\r
+\r
 70-B3-D5   (hex)               EVCO SPA\r
 A80000-A80FFF     (base 16)            EVCO SPA\r
                                VIA FELTRE N. 81\r
@@ -8507,6 +8675,150 @@ F80000-F80FFF     (base 16)             Guan Show Technologe Co., Ltd.
                                BRUGES    33520\r
                                FR\r
 \r
+70-B3-D5   (hex)               Newtec A/S\r
+18F000-18FFFF     (base 16)            Newtec A/S\r
+                               Stærmosegårdsvej  18\r
+                               Odense SV  Region Syd  5230\r
+                               DK\r
+\r
+70-B3-D5   (hex)               AUDIO VISUAL DIGITAL SYSTEMS\r
+A7F000-A7FFFF     (base 16)            AUDIO VISUAL DIGITAL SYSTEMS\r
+                               PLOT NO.180 PHASE V SECTOR56, HSIIDC I.E KUNDLI SONEPAT\r
+                               SONEPAT  HARYANA  131028\r
+                               IN\r
+\r
+70-B3-D5   (hex)               DEUTA-WERKE GmbH\r
+1BF000-1BFFFF     (base 16)            DEUTA-WERKE GmbH\r
+                               Paffrather Str. 140\r
+                               Bergisch Gladbach  North Rhine-Westphalia  51465\r
+                               DE\r
+\r
+70-B3-D5   (hex)               Cetitec GmbH\r
+B36000-B36FFF     (base 16)            Cetitec GmbH\r
+                               Mannheimer Strasse 17\r
+                               Pforzheim    75179\r
+                               DE\r
+\r
+70-B3-D5   (hex)               DONG IL VISION Co., Ltd.\r
+038000-038FFF     (base 16)            DONG IL VISION Co., Ltd.\r
+                               #9 Ftrek tower, 11-25, Simindaero 327 beongil,Dongan-gu\r
+                               Anyangi-Si  Gyeonggi-Do  14055\r
+                               KR\r
+\r
+70-B3-D5   (hex)               Kamacho Scale Co., Ltd.\r
+385000-385FFF     (base 16)            Kamacho Scale Co., Ltd.\r
+                               2246 Mure\r
+                               Takamatsu-shi  Kagawa-ken  761-0196\r
+                               JP\r
+\r
+70-B3-D5   (hex)               Visual Robotics\r
+0F4000-0F4FFF     (base 16)            Visual Robotics\r
+                               38 Irving Rd\r
+                               Eugene  OR  97404\r
+                               US\r
+\r
+70-B3-D5   (hex)               Vessel Technology Ltd\r
+44D000-44DFFF     (base 16)            Vessel Technology Ltd\r
+                               Banchory Business Centre, Burn O'Bennie Road\r
+                               Banchory  Aberdeenshire  AB31 5ZU\r
+                               GB\r
+\r
+70-B3-D5   (hex)               Munters\r
+FA8000-FA8FFF     (base 16)            Munters\r
+                               Hasivim 18\r
+                               Pethch Tikva  Israel  4959376\r
+                               IL\r
+\r
+70-B3-D5   (hex)               TEX COMPUTER SRL \r
+6C2000-6C2FFF     (base 16)            TEX COMPUTER SRL \r
+                               VIA MERCADANTE 35\r
+                               CATTOLICA   RIMINI   47841\r
+                               IT\r
+\r
+70-B3-D5   (hex)               TangRen C&S CO., Ltd\r
+3FC000-3FCFFF     (base 16)            TangRen C&S CO., Ltd\r
+                               3a-5d, Tingwei Daxia, Tingwei Industrial Park, No. 6, Liufang Road, Bao'an District\r
+                               Shenzhen  Guangdong  518052\r
+                               CN\r
+\r
+70-B3-D5   (hex)               LOTES TM OOO\r
+EA5000-EA5FFF     (base 16)            LOTES TM OOO\r
+                               Barklaya 22, str.1\r
+                               Moscow    121309\r
+                               RU\r
+\r
+70-B3-D5   (hex)               Yi An Electronics Co., Ltd\r
+F28000-F28FFF     (base 16)            Yi An Electronics Co., Ltd\r
+                               5F.-2, No. 81, Sec. 1, Xintai 5th Rd., Xizhi Dist\r
+                                New Taipei City    22101\r
+                               TW\r
+\r
+70-B3-D5   (hex)               Ariston Thermo s.p.a.\r
+3D6000-3D6FFF     (base 16)            Ariston Thermo s.p.a.\r
+                               Via Aristide Merloni 45\r
+                               Fabriano  Ancona  60044\r
+                               IT\r
+\r
+70-B3-D5   (hex)               MG s.r.l.\r
+130000-130FFF     (base 16)            MG s.r.l.\r
+                               via Monte Bianco, 1\r
+                               Solbiate Olona  VA  21058\r
+                               IT\r
+\r
+70-B3-D5   (hex)               DORLET SAU\r
+639000-639FFF     (base 16)            DORLET SAU\r
+                               Albert Eistein 34\r
+                               Alava  SPAIN  01510\r
+                               ES\r
+\r
+70-B3-D5   (hex)               OOO ORION-R\r
+047000-047FFF     (base 16)            OOO ORION-R\r
+                               Novoselov str., 40A, room N200\r
+                               Ryazan    390048\r
+                               RU\r
+\r
+70-B3-D5   (hex)               Glory Technology Service Inc.\r
+801000-801FFF     (base 16)            Glory Technology Service Inc.\r
+                               3F., No.43-1, Ln. 11, Sec. 6, Minquan E. Rd\r
+                               Taipei City   Neihu Dist  114\r
+                               TW\r
+\r
+70-B3-D5   (hex)               Toolplanet Co., Ltd.\r
+4B5000-4B5FFF     (base 16)            Toolplanet Co., Ltd.\r
+                               43-2 Himigaike-cho\r
+                               Gifu-shi  Gifu  500-8122\r
+                               JP\r
+\r
+70-B3-D5   (hex)               Postmark Incorporated \r
+CBB000-CBBFFF     (base 16)            Postmark Incorporated \r
+                               3197 Duncan Lane\r
+                               San Luis Obispo  CA  93401\r
+                               US\r
+\r
+70-B3-D5   (hex)               Abbott Diagnostics Technologies AS\r
+53F000-53FFFF     (base 16)            Abbott Diagnostics Technologies AS\r
+                               P. O.  Box 6863 Rodeløkka\r
+                               Oslo  Oslo  0504\r
+                               NO\r
+\r
+70-B3-D5   (hex)               Surion (Pty) Ltd\r
+7FC000-7FCFFF     (base 16)            Surion (Pty) Ltd\r
+                               205 Park Corner, 2 Bolton road, Rosebank\r
+                               JOHANNESBURG NORTH  Gauteng  2193\r
+                               ZA\r
+\r
+70-B3-D5   (hex)               REO AG\r
+8E7000-8E7FFF     (base 16)            REO AG\r
+                               Brühlerstr. 100\r
+                               Solingen    42657\r
+                               DE\r
+\r
+70-B3-D5   (hex)               GIORDANO CONTROLS SPA\r
+95D000-95DFFF     (base 16)            GIORDANO CONTROLS SPA\r
+                               VIA PARALLELA 2/4\r
+                               VILLA BARTOLOMEA  IT  37049\r
+                               IT\r
+\r
 70-B3-D5   (hex)               System West dba ICS Electronics\r
 E06000-E06FFF     (base 16)            System West dba ICS Electronics\r
                                7034 Commerce Circle Suite A\r
@@ -8828,12 +9140,6 @@ D36000-D36FFF     (base 16)              Insitu Inc.
                                Bingen  WA  98605\r
                                US\r
 \r
-70-B3-D5   (hex)               Pano0ramic Power\r
-669000-669FFF     (base 16)            Pano0ramic Power\r
-                               15 Atir Yeda\r
-                               Kfar Saba    4464312\r
-                               IL\r
-\r
 00-1B-C5   (hex)               Private\r
 0B8000-0B8FFF     (base 16)            Private\r
 \r
@@ -8939,12 +9245,6 @@ A7D000-A7DFFF     (base 16)              Prior Scientific Instruments Ltd
                                Cambridge  Cambridgeshire  CB21 5ET\r
                                GB\r
 \r
-70-B3-D5   (hex)               Taejin InforTech\r
-A75000-A75FFF     (base 16)            Taejin InforTech\r
-                               40, Imi-ro, A-411\r
-                               Uiwang-si  Gyeonggi-do  16006\r
-                               KR\r
-\r
 70-B3-D5   (hex)               AUTOMATIZACION Y CONECTIVIDAD SA DE CV\r
 59B000-59BFFF     (base 16)            AUTOMATIZACION Y CONECTIVIDAD SA DE CV\r
                                LA GARITA ANDADOR 6 DUPLEX 1 CASA 2\r
@@ -9281,12 +9581,6 @@ C05000-C05FFF     (base 16)              KST technology
                                Songpa-gu  Seoul  05636\r
                                KR\r
 \r
-70-B3-D5   (hex)               Coheros Oy\r
-D2E000-D2EFFF     (base 16)            Coheros Oy\r
-                               Korkeakoulunkatu 1\r
-                               Tampere    33720\r
-                               FI\r
-\r
 70-B3-D5   (hex)               EA Elektroautomatik GmbH & Co. KG\r
 26C000-26CFFF     (base 16)            EA Elektroautomatik GmbH & Co. KG\r
                                Helmholtzstraße 31-33\r
@@ -12722,6 +13016,12 @@ CFB000-CFBFFF     (base 16)            Screen Innovations
                                Bingen  WA  98605\r
                                US\r
 \r
+70-B3-D5   (hex)               German Power GmbH\r
+C31000-C31FFF     (base 16)            German Power GmbH\r
+                               Freiburger Strasse 7\r
+                               Pforzheim    75179\r
+                               DE\r
+\r
 70-B3-D5   (hex)               Lyse AS\r
 F23000-F23FFF     (base 16)            Lyse AS\r
                                Breiflåtveien 18\r
@@ -12734,6 +13034,234 @@ F23000-F23FFF     (base 16)           Lyse AS
                                Paris    75006\r
                                FR\r
 \r
+70-B3-D5   (hex)               Hefei STAROT Technology Co.,Ltd\r
+4D3000-4D3FFF     (base 16)            Hefei STAROT Technology Co.,Ltd\r
+                               406, 4th Floor, Quality Control Building, Saipu Science Park, No. 6 Yunfei Road, High-tech Zone\r
+                               hefei  anhui  230000\r
+                               CN\r
+\r
+70-B3-D5   (hex)               SysCom Automationstechnik GmbH\r
+117000-117FFF     (base 16)            SysCom Automationstechnik GmbH\r
+                               An der Lehmkaute 13\r
+                               Bad Marienberg  Rheinland-Pfalz  56470\r
+                               DE\r
+\r
+70-B3-D5   (hex)               JFA Electronics Industry and Commerce EIRELI\r
+5F7000-5F7FFF     (base 16)            JFA Electronics Industry and Commerce EIRELI\r
+                               Rua Flor das Pedras, 175\r
+                               Belo Horizonte  Minas Gerais  30810-000\r
+                               BR\r
+\r
+70-B3-D5   (hex)               Hubbell Power Systems\r
+858000-858FFF     (base 16)            Hubbell Power Systems\r
+                               353 Powerville Road\r
+                               Boonton Township  NJ  07005\r
+                               US\r
+\r
+70-B3-D5   (hex)               Walton Hi-Tech Industries Ltd.\r
+E5C000-E5CFFF     (base 16)            Walton Hi-Tech Industries Ltd.\r
+                               HOLDING NO. I-65/2, WARD NO-07\r
+                               CHANDRA, KALIAKOIR, GAZIPUR.    1750\r
+                               BD\r
+\r
+70-B3-D5   (hex)               aquila biolabs GmbH\r
+7DB000-7DBFFF     (base 16)            aquila biolabs GmbH\r
+                               Arnold-Sommerfeld-Ring 2\r
+                               Baesweiler  NRW  52499\r
+                               DE\r
+\r
+70-B3-D5   (hex)               Sicon srl\r
+C82000-C82FFF     (base 16)            Sicon srl\r
+                               Via Sila 1/3\r
+                               Isola Vicentina  Vicenza  36033\r
+                               IT\r
+\r
+70-B3-D5   (hex)               Flextronics International Kft\r
+699000-699FFF     (base 16)            Flextronics International Kft\r
+                               38. Zrinyi Str.\r
+                               Zalaegerszeg  Zala  8900\r
+                               HU\r
+\r
+70-B3-D5   (hex)               LGE\r
+DAE000-DAEFFF     (base 16)            LGE\r
+                               10, Magokjungang 10-ro, Gangseo-gu\r
+                               Seoul    07796\r
+                               KR\r
+\r
+70-B3-D5   (hex)               Jonsa Australia Pty Ltd\r
+335000-335FFF     (base 16)            Jonsa Australia Pty Ltd\r
+                               Unit D2 3-29 Birnie Ave\r
+                               Lidcombe  NSW  2141\r
+                               AU\r
+\r
+70-B3-D5   (hex)               GreenWake Technologies\r
+467000-467FFF     (base 16)            GreenWake Technologies\r
+                               56 boulevard Niels Bohr, CEI2\r
+                               Villeurbanne    69100\r
+                               FR\r
+\r
+70-B3-D5   (hex)               shenzhen suofeixiang technology Co.,Ltd\r
+EEB000-EEBFFF     (base 16)            shenzhen suofeixiang technology Co.,Ltd\r
+                               sales09@sfxhd.com\r
+                               shenzhen    518000\r
+                               CN\r
+\r
+70-B3-D5   (hex)               RCH Vietnam Limited Liability Company\r
+97D000-97DFFF     (base 16)            RCH Vietnam Limited Liability Company\r
+                               Workshop F.01B-2, Lot No. F.01B Long Hau\r
+                               Ho Chi Minh City  Ho Chi Minh  70000\r
+                               VN\r
+\r
+70-B3-D5   (hex)               SNK, Inc.\r
+E12000-E12FFF     (base 16)            SNK, Inc.\r
+                               Rm 302 Inobiz park, 1646, Yuseong-daero, Yuseong-gu\r
+                               Daejeon    34054\r
+                               KR\r
+\r
+70-B3-D5   (hex)               SYLink Technologie\r
+466000-466FFF     (base 16)            SYLink Technologie\r
+                               18 rue de la conche\r
+                               Mirefleurs  Auvergne  63730\r
+                               FR\r
+\r
+70-B3-D5   (hex)               silicom\r
+F64000-F64FFF     (base 16)            silicom\r
+                               14 Atir-Yeda St/\r
+                               Kfar-Sava  Israel  44000\r
+                               IL\r
+\r
+70-B3-D5   (hex)               NSP Europe Ltd\r
+18A000-18AFFF     (base 16)            NSP Europe Ltd\r
+                               Unit 5, Devonshire Business Park\r
+                               Borehamwood  Hert  WD6 1NA\r
+                               GB\r
+\r
+70-B3-D5   (hex)               Microchip Technology Germany II GmbH&Co.KG\r
+77F000-77FFFF     (base 16)            Microchip Technology Germany II GmbH&Co.KG\r
+                               Emmy-Noether-Straße 14\r
+                               Karlsruhe    76131\r
+                               DE\r
+\r
+70-B3-D5   (hex)               Trust Automation\r
+C98000-C98FFF     (base 16)            Trust Automation\r
+                               125 Venture Dr\r
+                               San Luis Obispo  CA  93401\r
+                               US\r
+\r
+70-B3-D5   (hex)               Kospel S.A.\r
+249000-249FFF     (base 16)            Kospel S.A.\r
+                               Olchowa 1\r
+                               Koszalin    75-136\r
+                               PL\r
+\r
+70-B3-D5   (hex)               Coheros Oy\r
+D2E000-D2EFFF     (base 16)            Coheros Oy\r
+                               Tammukkakatu 6\r
+                               Nokia    37130\r
+                               FI\r
+\r
+70-B3-D5   (hex)               Gogo Business Aviation\r
+E24000-E24FFF     (base 16)            Gogo Business Aviation\r
+                               105 Edgeview Dr., Suite 300\r
+                               Broomfield  CO  80021\r
+                               US\r
+\r
+70-B3-D5   (hex)               Taejin InfoTech\r
+A75000-A75FFF     (base 16)            Taejin InfoTech\r
+                               40, Imi-ro, A-411\r
+                               Uiwang-si  Gyeonggi-do  16006\r
+                               KR\r
+\r
+70-B3-D5   (hex)               ARCLAN'SYSTEM\r
+25C000-25CFFF     (base 16)            ARCLAN'SYSTEM\r
+                               1140 rue Ampère - Actimart II - Lot 9\r
+                               AIX EN PROVENCE    13290\r
+                               FR\r
+\r
+70-B3-D5   (hex)               Smart Embedded Systems\r
+A09000-A09FFF     (base 16)            Smart Embedded Systems\r
+                               6701  Koll Center Parkway #250\r
+                               Pleasonton  CA  94566\r
+                               US\r
+\r
+70-B3-D5   (hex)               Guan Show Technologe Co., Ltd.\r
+F6A000-F6AFFF     (base 16)            Guan Show Technologe Co., Ltd.\r
+                               No.127, Jianguo 1st Rd., Lingya Dist.\r
+                                Kaohsiung City     802\r
+                               TW\r
+\r
+70-B3-D5   (hex)               LLC Sarov Innovative Technologies (WIZOLUTION)\r
+50F000-50FFFF     (base 16)            LLC Sarov Innovative Technologies (WIZOLUTION)\r
+                               RUSSIAN FEDERATION, Nizhny Novgorod region, Varlamovskaya road, 7, build 2\r
+                               Sarov  Nizhny Novgorod  607188\r
+                               RU\r
+\r
+70-B3-D5   (hex)               SPX Radiodetection\r
+A77000-A77FFF     (base 16)            SPX Radiodetection\r
+                               Western Drive\r
+                               Bristol  Avon  BS14 0AF\r
+                               GB\r
+\r
+70-B3-D5   (hex)               LM-Instruments Oy\r
+5AC000-5ACFFF     (base 16)            LM-Instruments Oy\r
+                               Norrbyn rantatie 8\r
+                               Parainen    21600\r
+                               FI\r
+\r
+70-B3-D5   (hex)               Fuhr GmbH Filtertechnik\r
+DBB000-DBBFFF     (base 16)            Fuhr GmbH Filtertechnik\r
+                               Am Weinkastell 14\r
+                               Klein-Winternheim  Rheinland-Pfalz  55270\r
+                               DE\r
+\r
+70-B3-D5   (hex)               Sanmina Israel\r
+C18000-C18FFF     (base 16)            Sanmina Israel\r
+                               Koren Industrial Zone , POBox 102\r
+                               Maalot  Israel  2101002\r
+                               IL\r
+\r
+70-B3-D5   (hex)               INVISSYS\r
+AD4000-AD4FFF     (base 16)            INVISSYS\r
+                               25 rue marcel issartier\r
+                               merignac    33700\r
+                               FR\r
+\r
+70-B3-D5   (hex)               Panoramic Power\r
+669000-669FFF     (base 16)            Panoramic Power\r
+                               15 Atir Yeda\r
+                               Kfar Saba    4464312\r
+                               IL\r
+\r
+70-B3-D5   (hex)               Panoramic Power\r
+06D000-06DFFF     (base 16)            Panoramic Power\r
+                               Atir Yeda 15\r
+                               Kfar Saba    4464312\r
+                               IL\r
+\r
+70-B3-D5   (hex)               Avlinkpro\r
+2C1000-2C1FFF     (base 16)            Avlinkpro\r
+                               380 US Highway 46\r
+                               Totowa  NJ  07512\r
+                               US\r
+\r
+70-B3-D5   (hex)               DECYBEN\r
+683000-683FFF     (base 16)            DECYBEN\r
+                               170 Rue Raymond Losserand\r
+                               Paris    75014\r
+                               FR\r
+\r
+70-B3-D5   (hex)               C4I Systems Ltd\r
+5C6000-5C6FFF     (base 16)            C4I Systems Ltd\r
+                               Unit 1Twyford Court\r
+                               Hereford  Herefordshire  HR2 6JR\r
+                               GB\r
+\r
+70-B3-D5   (hex)               Knowledge Resources GmbH\r
+C36000-C36FFF     (base 16)            Knowledge Resources GmbH\r
+                               Ackerstrasse 30\r
+                               Bsel  BS  4057\r
+                               CH\r
+\r
 70-B3-D5   (hex)               YUYAMA MFG Co.,Ltd\r
 BBB000-BBBFFF     (base 16)            YUYAMA MFG Co.,Ltd\r
                                3-3-1\r
@@ -16859,12 +17387,6 @@ BB2000-BB2FFF     (base 16)            Mettler Toledo
                                Lutz  FL  33558\r
                                US\r
 \r
-70-B3-D5   (hex)               SFR\r
-B12000-B12FFF     (base 16)            SFR\r
-                               12 rue jean-philippe Rameau CS 80001\r
-                               La plaine saint denis   FRANCE  93634\r
-                               FR\r
-\r
 70-B3-D5   (hex)               S.E.I. CO.,LTD.\r
 12D000-12DFFF     (base 16)            S.E.I. CO.,LTD.\r
                                59 Nirayama Tada\r
@@ -16997,6 +17519,210 @@ F4C000-F4CFFF     (base 16)           PolyTech A/S
                                Herning  Herning  7400\r
                                DK\r
 \r
+70-B3-D5   (hex)               Shanghai Tiancheng Communication Technology Corporation\r
+1C3000-1C3FFF     (base 16)            Shanghai Tiancheng Communication Technology Corporation\r
+                               No.618,Guangxing Rd.,Songjiang \r
+                               shanghai    200090\r
+                               CN\r
+\r
+70-B3-D5   (hex)               T&M Media Pty Ltd\r
+B41000-B41FFF     (base 16)            T&M Media Pty Ltd\r
+                               6, 476 Gardeners Road\r
+                               Alexandria  NSW  2015\r
+                               AU\r
+\r
+70-B3-D5   (hex)               SAMBO HITECH\r
+282000-282FFF     (base 16)            SAMBO HITECH\r
+                               469,Seokjung-ro,Namdong-Gu\r
+                               Incheon    21501\r
+                               KR\r
+\r
+70-B3-D5   (hex)               M.A.C. Solutions (UK) Ltd\r
+F9F000-F9FFFF     (base 16)            M.A.C. Solutions (UK) Ltd\r
+                               Units 6-7 Kingfisher Business Park, Arthur Street\r
+                               Redditch  Worcestershire  B98 8LG\r
+                               GB\r
+\r
+70-B3-D5   (hex)               Shenzhen CAMERAY ELECTRONIC CO., LTD\r
+1E2000-1E2FFF     (base 16)            Shenzhen CAMERAY ELECTRONIC CO., LTD\r
+                               4-5FL, Building 1, Guanghui Science, and Technology Park; Minqing Road, Longhua Town\r
+                               shenzhen  GD  518109\r
+                               CN\r
+\r
+70-B3-D5   (hex)               Vulcan Wireless Inc.\r
+E4D000-E4DFFF     (base 16)            Vulcan Wireless Inc.\r
+                               2218 Faraday Ave Suite 110\r
+                               Carlsbad  CA  92008\r
+                               US\r
+\r
+70-B3-D5   (hex)               ERA TOYS LIMITED\r
+193000-193FFF     (base 16)            ERA TOYS LIMITED\r
+                               Room 505, 5th Floor, Beverley Commercial Centre, 87-105 Chatham Road South\r
+                               Tsim Sha Tsui  Kowloon  0000\r
+                               HK\r
+\r
+70-B3-D5   (hex)               Scorpion Precision Industry (HK)CO. Ltd.\r
+02B000-02BFFF     (base 16)            Scorpion Precision Industry (HK)CO. Ltd.\r
+                               16th Floor, Excelsior Industrial Building,68-76 Sha Tsui Road,\r
+                               Tsuen Wan  New Territories  999077\r
+                               HK\r
+\r
+70-B3-D5   (hex)               Cryptotronix LLC\r
+0DB000-0DBFFF     (base 16)            Cryptotronix LLC\r
+                               P.O. Box 273029\r
+                               Fort Collins  CO  80525\r
+                               US\r
+\r
+70-B3-D5   (hex)               MIVO Technology AB\r
+1D5000-1D5FFF     (base 16)            MIVO Technology AB\r
+                               Hornsbergsvägen 28\r
+                               Stockholm    11215\r
+                               SE\r
+\r
+70-B3-D5   (hex)               A&T Corporation\r
+32E000-32EFFF     (base 16)            A&T Corporation\r
+                               2023-1\r
+                               Endo, Fujisawa, Kanagawa    252-0816\r
+                               JP\r
+\r
+70-B3-D5   (hex)               TOMEI TSUSHIN KOGYO CO,.LTD\r
+FB1000-FB1FFF     (base 16)            TOMEI TSUSHIN KOGYO CO,.LTD\r
+                               100-3, Amaike Kodacho\r
+                               Inazawa Shi  Aichi ken  4928274\r
+                               JP\r
+\r
+70-B3-D5   (hex)               DogWatch Inc\r
+1E7000-1E7FFF     (base 16)            DogWatch Inc\r
+                               10 Michigan Drive\r
+                               Natick    01760\r
+                               US\r
+\r
+70-B3-D5   (hex)               RCH Vietnam Limited Liability Company\r
+C09000-C09FFF     (base 16)            RCH Vietnam Limited Liability Company\r
+                               Workshop F.01B-2, Lot No. F.01B Long Hau\r
+                               Ho Chi Minh City  Ho Chi Minh  70000\r
+                               VN\r
+\r
+70-B3-D5   (hex)               Copper Labs, Inc.\r
+F69000-F69FFF     (base 16)            Copper Labs, Inc.\r
+                               3015 Sterling Circle #200\r
+                               Boulder  CO  80301\r
+                               US\r
+\r
+70-B3-D5   (hex)               SHENZHEN HUINENGYUAN Technology Co., Ltd\r
+A83000-A83FFF     (base 16)            SHENZHEN HUINENGYUAN Technology Co., Ltd\r
+                               Room 206, 3 Building, Hongwanchuangke Center, Gushu, Xixiang, Baoan District\r
+                               Shenzhen  Guangdong  518126\r
+                               CN\r
+\r
+70-B3-D5   (hex)               Vars Technology \r
+C94000-C94FFF     (base 16)            Vars Technology \r
+                               Squires gate industrial estate Unit 14\r
+                               Blackpool  lancashire   FY4 3RN\r
+                               GB\r
+\r
+70-B3-D5   (hex)               Burk Technology\r
+641000-641FFF     (base 16)            Burk Technology\r
+                               7 Beaver Brook road\r
+                               Littleton  MA  01460\r
+                               US\r
+\r
+70-B3-D5   (hex)               Inventeq B.V.\r
+529000-529FFF     (base 16)            Inventeq B.V.\r
+                               Ravenlaan 27\r
+                               Blaricum    1261WT\r
+                               NL\r
+\r
+70-B3-D5   (hex)               Grossenbacher Systeme AG\r
+B75000-B75FFF     (base 16)            Grossenbacher Systeme AG\r
+                               Spinnereistrasse 10\r
+                               St. Gallen    9008\r
+                               CH\r
+\r
+70-B3-D5   (hex)               ITsynergy Ltd\r
+D2A000-D2AFFF     (base 16)            ITsynergy Ltd\r
+                               9 Bonhill Street\r
+                               London    EC2A 4DJ\r
+                               GB\r
+\r
+70-B3-D5   (hex)               Vaunix Technology Corporation\r
+EE6000-EE6FFF     (base 16)            Vaunix Technology Corporation\r
+                               7 New Pasture Rd\r
+                               Newburyport  MA  01950\r
+                               US\r
+\r
+70-B3-D5   (hex)               chargeBIG\r
+869000-869FFF     (base 16)            chargeBIG\r
+                               Pragstraße 26-46\r
+                               Stuttgart    70376\r
+                               DE\r
+\r
+70-B3-D5   (hex)               Portrait Displays, Inc.\r
+D77000-D77FFF     (base 16)            Portrait Displays, Inc.\r
+                               6663 OWENS DR\r
+                               PLEASANTON  CA  94588\r
+                               US\r
+\r
+70-B3-D5   (hex)               Sprintshield d.o.o.\r
+B03000-B03FFF     (base 16)            Sprintshield d.o.o.\r
+                               Marina Getaldi?a 3\r
+                               Velika Gorica    10410\r
+                               HR\r
+\r
+70-B3-D5   (hex)               Tricom Research Inc.\r
+601000-601FFF     (base 16)            Tricom Research Inc.\r
+                               17791 Sky Park Circle Suite GHJ\r
+                               Irvine  CA  92614\r
+                               US\r
+\r
+70-B3-D5   (hex)               Mictrotrac Retsch GmbH\r
+F09000-F09FFF     (base 16)            Mictrotrac Retsch GmbH\r
+                               Retsch-Allee 1-5\r
+                               Haan  NRW  42781\r
+                               DE\r
+\r
+70-B3-D5   (hex)               KeyProd\r
+473000-473FFF     (base 16)            KeyProd\r
+                               66 avenue des Champs Elysées\r
+                               Paris    77008\r
+                               FR\r
+\r
+70-B3-D5   (hex)               MB connect line GmbH Fernwartungssysteme\r
+6D7000-6D7FFF     (base 16)            MB connect line GmbH Fernwartungssysteme\r
+                               Winnettener Straße 6\r
+                               Dinkelsbuehl  Bavaria  91550\r
+                               DE\r
+\r
+70-B3-D5   (hex)               EarTex\r
+E01000-E01FFF     (base 16)            EarTex\r
+                               41 Corsham Street\r
+                               London  England  N1 6DR\r
+                               GB\r
+\r
+70-B3-D5   (hex)               AVL DiTEST GmbH\r
+78D000-78DFFF     (base 16)            AVL DiTEST GmbH\r
+                               Alte Poststrasse 156\r
+                               Graz    8020\r
+                               AT\r
+\r
+70-B3-D5   (hex)               Scharco Elektronik GmbH\r
+C72000-C72FFF     (base 16)            Scharco Elektronik GmbH\r
+                               Tilsiter Strasse 8\r
+                               Wuppertal  NRW  42277\r
+                               DE\r
+\r
+70-B3-D5   (hex)               WARECUBE,INC\r
+AD3000-AD3FFF     (base 16)            WARECUBE,INC\r
+                               #A-811, 142-10, Saneop-ro, 156beon-gil, Gwonseon-gu\r
+                               Suwon-si    16648\r
+                               KR\r
+\r
+70-B3-D5   (hex)               myUpTech AB\r
+FC3000-FC3FFF     (base 16)            myUpTech AB\r
+                               Box 14\r
+                               Markaryd    28532\r
+                               SE\r
+\r
 70-B3-D5   (hex)               DISMUNTEL SAL\r
 92C000-92CFFF     (base 16)            DISMUNTEL SAL\r
                                Pol ind cotes\r
@@ -18503,12 +19229,6 @@ E18000-E18FFF     (base 16)            Plasmapp Co.,Ltd.
                                Eckental  Bavaria  90542\r
                                DE\r
 \r
-70-B3-D5   (hex)               Pano0ramic Power\r
-53A000-53AFFF     (base 16)            Pano0ramic Power\r
-                               15 Atir Yeda\r
-                               Kfar Saba    4464312\r
-                               IL\r
-\r
 70-B3-D5   (hex)               ND METER\r
 68C000-68CFFF     (base 16)            ND METER\r
                                228 BOLTON ROAD\r
@@ -21292,3 +22012,141 @@ F5D000-F5DFFF     (base 16)           Potter Electric Signal Co. LLC
                                via Cupa Vicinale S.Aniello, 88\r
                                Naples    80146\r
                                IT\r
+\r
+70-B3-D5   (hex)               ITK Dr. Kassen GmbH\r
+58A000-58AFFF     (base 16)            ITK Dr. Kassen GmbH\r
+                               Beim Eberacker 3\r
+                               D-35633 Lahnau    \r
+                               DE\r
+\r
+70-B3-D5   (hex)               RCH Vietnam Limited Liability Company\r
+88E000-88EFFF     (base 16)            RCH Vietnam Limited Liability Company\r
+                               Workshop F.01B-2, Lot No. F.01B Long Hau\r
+                               Ho Chi Minh City  Ho Chi Minh  70000\r
+                               VN\r
+\r
+70-B3-D5   (hex)               Privafy, Inc\r
+A6A000-A6AFFF     (base 16)            Privafy, Inc\r
+                               2 Burlington Woods Dr. Suite 200\r
+                               Burlington  MA  01803\r
+                               US\r
+\r
+70-B3-D5   (hex)               Contec Americas Inc.\r
+5D2000-5D2FFF     (base 16)            Contec Americas Inc.\r
+                               3991 Sarno Rd\r
+                               Melbourne  FL  32934\r
+                               US\r
+\r
+70-B3-D5   (hex)               elements\r
+62D000-62DFFF     (base 16)            elements\r
+                               Townsgate Road Suite 200 \r
+                               Westlake Village  CA  91361\r
+                               US\r
+\r
+70-B3-D5   (hex)               DAT Informatics Pvt Ltd\r
+244000-244FFF     (base 16)            DAT Informatics Pvt Ltd\r
+                               Plot No 109 HPSIDC Industria Estate Davni, Baddi\r
+                               Baddi  HIMACHAL PRADESH  173205\r
+                               IN\r
+\r
+70-B3-D5   (hex)               Abbott Diagnostics Technologies AS\r
+6C6000-6C6FFF     (base 16)            Abbott Diagnostics Technologies AS\r
+                               P. O.  Box 6863 Rodeløkka\r
+                               Oslo  Oslo  0504\r
+                               NO\r
+\r
+70-B3-D5   (hex)               Gamber Johnson-LLC\r
+E34000-E34FFF     (base 16)            Gamber Johnson-LLC\r
+                               3001 Borham Ave\r
+                               Stevens Point  WI  54481\r
+                               US\r
+\r
+70-B3-D5   (hex)               RCH Vietnam Limited Liability Company\r
+6BD000-6BDFFF     (base 16)            RCH Vietnam Limited Liability Company\r
+                               Workshop F.01B-2, Lot No. F.01B Long Hau\r
+                               Ho Chi Minh City  Ho Chi Minh  70000\r
+                               VN\r
+\r
+70-B3-D5   (hex)               YUYAMA MFG Co.,Ltd\r
+1F2000-1F2FFF     (base 16)            YUYAMA MFG Co.,Ltd\r
+                               3-3-1\r
+                               TOYONAKASHI  OSAKA  561-0841\r
+                               JP\r
+\r
+70-B3-D5   (hex)               QUALITTEQ LLC\r
+614000-614FFF     (base 16)            QUALITTEQ LLC\r
+                               16th Parkovaya 26/1\r
+                               Moscow    105484\r
+                               RU\r
+\r
+70-B3-D5   (hex)               YUYAMA MFG Co.,Ltd\r
+C2B000-C2BFFF     (base 16)            YUYAMA MFG Co.,Ltd\r
+                               3-3-1\r
+                               TOYONAKASHI  OSAKA  561-0841\r
+                               JP\r
+\r
+70-B3-D5   (hex)               Adcole Maryland Aerospace\r
+922000-922FFF     (base 16)            Adcole Maryland Aerospace\r
+                               669 Forest St\r
+                               Marlborough  MA  01752\r
+                               US\r
+\r
+70-B3-D5   (hex)               eSMART Technologies SA\r
+979000-979FFF     (base 16)            eSMART Technologies SA\r
+                               Chemin de la Rueyre, 118\r
+                               Renens VD    1020\r
+                               CH\r
+\r
+70-B3-D5   (hex)               Duplomatic MS spa\r
+DE1000-DE1FFF     (base 16)            Duplomatic MS spa\r
+                               Via Re Depaolini 24\r
+                               Parabiago  Milan  20015\r
+                               IT\r
+\r
+70-B3-D5   (hex)               Axnes AS\r
+65F000-65FFFF     (base 16)            Axnes AS\r
+                               Terje Løvåsvei 1\r
+                               Grimstad    4879\r
+                               NO\r
+\r
+70-B3-D5   (hex)               Nanjing Pingguang Electronic Technology Co., Ltd\r
+541000-541FFF     (base 16)            Nanjing Pingguang Electronic Technology Co., Ltd\r
+                               B30/B31 4th Floor, Building#11, Shengtai Road, JiangNing District\r
+                               NanJing    211100\r
+                               CN\r
+\r
+70-B3-D5   (hex)               thingdust AG\r
+3C1000-3C1FFF     (base 16)            thingdust AG\r
+                               Moosstrasse 7\r
+                               Lucerne  Lucerne  6003\r
+                               CH\r
+\r
+70-B3-D5   (hex)               ALVAT s.r.o.\r
+369000-369FFF     (base 16)            ALVAT s.r.o.\r
+                               Chodovska 228/3\r
+                               Praha 4    14100\r
+                               CZ\r
+\r
+70-B3-D5   (hex)               PHYZHON Health Inc\r
+744000-744FFF     (base 16)            PHYZHON Health Inc\r
+                               180 Blue Ravine Road, suite A\r
+                               Folsom  CA  95630\r
+                               US\r
+\r
+70-B3-D5   (hex)               PCB Piezotronics\r
+4CA000-4CAFFF     (base 16)            PCB Piezotronics\r
+                               3425 Walden Avenue\r
+                               Depew  NY  14043\r
+                               US\r
+\r
+70-B3-D5   (hex)               Panoramic Power\r
+53A000-53AFFF     (base 16)            Panoramic Power\r
+                               15 Atir Yeda\r
+                               Kfar Saba    4464312\r
+                               IL\r
+\r
+70-B3-D5   (hex)               STEP sarl\r
+481000-481FFF     (base 16)            STEP sarl\r
+                               11, avenue Aristide Berges\r
+                               LANCEY  ISERE  38190\r
+                               FR\r
index 1f73a8bbb69e13d9637888d762b749d8474a2f46..4df6dabf89f6ef94f11b214d08d20487c26f59fb 100644 (file)
@@ -13,6 +13,7 @@ hwdb_files = files('''
         20-net-ifname.hwdb
         20-vmbus-class.hwdb
         60-evdev.hwdb
+        60-input-id.hwdb
         60-keyboard.hwdb
         60-sensor.hwdb
         70-joystick.hwdb
index b5395b11fe53627aa9f472812ee66213b7e0121d..579c45fda0218281080d94aef71493e1d171eeb3 100755 (executable)
@@ -128,6 +128,7 @@ def property_grammar():
              ('KEYBOARD_LED_CAPSLOCK', Literal('0')),
              ('ACCEL_MOUNT_MATRIX', mount_matrix),
              ('ACCEL_LOCATION', Or(('display', 'base'))),
+             ('PROXIMITY_NEAR_LEVEL', INTEGER),
             )
     fixed_props = [Literal(name)('NAME') - Suppress('=') - val('VALUE')
                    for name, val in props]
index bb92533d91e0b0070961a0c439e8cada899be2f3..8bbe57abbc318e4d71b8a79af8cd42320f376853 100644 (file)
@@ -1,8 +1,8 @@
 #
 #      List of PCI ID's
 #
-#      Version: 2019.11.26
-#      Date:    2019-11-26 03:15:03
+#      Version: 2020.03.05
+#      Date:    2020-03-05 03:15:04
 #
 #      Maintained by Albert Pool, Martin Mares, and other volunteers from
 #      the PCI ID Project at https://pci-ids.ucw.cz/.
@@ -67,6 +67,7 @@
 # 018a is not LevelOne but there is a board misprogrammed
 018a  LevelOne
        0106  FPC-0106TX misprogrammed [RTL81xx]
+01de  Oxide Computer Company
 # 021b is not Compaq but there is a board misprogrammed
 021b  Compaq Computer Corporation
        8139  HNE-300 (RealTek RTL8139c) [iPaq Networking]
                1028 1fd1  PERC H730P MX
                17aa 1052  ThinkServer RAID 720i
                17aa 1053  ThinkServer RAID 720ix
+               1bd4 0014  12G SAS3108 2G
+               1bd4 0015  12G SAS3108 4G
                1d49 0600  ThinkSystem RAID 730-8i 1GB Cache PCIe 12Gb Adapter
                1d49 0608  ThinkSystem RAID 730-8i 2GB Flash PCIe 12Gb Adapter
                1d49 0609  ThinkSystem RAID 730-8i 4GB Flash PCIe 12Gb Adapter
                1028 1f1f  PERC H200 Modular
                1028 1f20  PERC H200 Embedded
                1028 1f22  PERC H200 Internal Tape Adapter
+# Fujitsu D2607 SAS2008 HBA controller
+               1734 1177  HBA Ctrl SAS 6G 0/1 [D2607]
+               1bd4 000d  6G SAS2008IT
+               1bd4 000e  6G SAS2008IR
+               1bd4 000f  6G SAS2008IT SA5248
+               1bd4 0010  6G SAS2008IR SA5248
                8086 350f  RMS2LL040 RAID Controller
                8086 3700  SSD 910 Series
        0073  MegaRAID SAS 2008 [Falcon]
                1590 0041  H220i
                1590 0042  H221 / 9207-8e
                1590 0044  H220i
+               1bd4 0009  6G SAS2308IR
+               1bd4 000a  6G SAS2308IT
                8086 3000  RS25GB008 RAID Controller
                8086 3060  RS25FB044 RAID Controller
                8086 3516  RMS25JB080 RAID Controller
                1028 1fd3  HBA330 MMZ
 # Supermicro AOC-S3008L-L8e uses 0808 for their SAS3008 SAS controller
                15d9 0808  AOC-S3008L-L8e
+               1bd4 000b  12G SAS3008IR
+               1bd4 000c  12G SAS3008IT
                1bd4 0011  Inspur 12Gb 8i-3008 IT SAS HBA
+               1bd4 0012  12Gb SAS3008IR UDM
+               1bd4 0026  12G SAS3008IT RACK
+               1bd4 0027  12G SAS3008IMR RACK
+               1bd4 0028  12G SAS3008IR RACK
        00ab  SAS3516 Fusion-MPT Tri-Mode RAID On Chip (ROC)
 # 8 Internal and 8 External port channel 9400 HBA
                1000 3040  HBA 9400-8i8e
        1306  Kaveri
        1307  Kaveri
        1308  Kaveri HDMI/DP Audio Controller
+               17aa 3988  Z50-75
        1309  Kaveri [Radeon R6/R7 Graphics]
+               17aa 3830  Z50-75
        130a  Kaveri [Radeon R6 Graphics]
        130b  Kaveri [Radeon R4 Graphics]
        130c  Kaveri [Radeon R7 Graphics]
        1561  Anubis
        15d8  Picasso
                103c 8615  Pavilion Laptop 15-cw1xxx
+               17aa 5124  ThinkPad E595
        15dd  Raven Ridge [Radeon Vega Series / Radeon Vega Mobile Series]
                103c 83c6  Radeon Vega 8 Mobile
                1458 d000  Radeon RX Vega 11
        15de  Raven/Raven2/Fenghuang HDMI/DP Audio Controller
                103c 8615  Pavilion Laptop 15-cw1xxx
+               17aa 5124  ThinkPad E595
        15df  Raven/Raven2/Fenghuang/Renoir Cryptographic Coprocessor
                103c 8615  Pavilion Laptop 15-cw1xxx
        15ff  Fenghuang [Zhongshan Subor Z+]
        4382  SB600 AC97 Audio
        4383  SBx00 Azalia (Intel HDA)
                1019 2120  A785GM-M
-               103c 1611  Pavilion DM1Z-3000
+               103c 1611  Pavilion dm1z-3000
                103c 280a  DC5750 Microtower
                1043 8230  M3A78-EH Motherboard
                1043 836c  M4A785TD Motherboard
                1458 b002  GA-MA770-DS3rev2.0 Motherboard
                1849 4390  Motherboard (one of many)
        4391  SB7x0/SB8x0/SB9x0 SATA Controller [AHCI mode]
+               103c 1609  ProLiant MicroServer N36L
                103c 1611  Pavilion DM1Z-3000
                1043 82ef  M3A78-EH Motherboard
                1043 8443  M5A88-V EVO
        4395  SB8x0/SB9x0 SATA Controller [Storage mode]
        4396  SB7x0/SB8x0/SB9x0 USB EHCI Controller
                1019 2120  A785GM-M
+               103c 1609  ProLiant MicroServer N36L
                103c 1611  Pavilion DM1Z-3000
                1043 82ef  M3A78-EH Motherboard
                1043 8443  M5A88-V EVO
                174b 1001  PURE Fusion Mini
        4397  SB7x0/SB8x0/SB9x0 USB OHCI0 Controller
                1019 2120  A785GM-M
+               103c 1609  ProLiant MicroServer N36L
                103c 1611  Pavilion DM1Z-3000
                1043 82ef  M3A78-EH Motherboard
                1043 8443  M5A88-V EVO
        439c  SB7x0/SB8x0/SB9x0 IDE Controller
                1002 4392  MSI MS-7713 motherboard
                1019 2120  A785GM-M
+               103c 1609  ProLiant MicroServer N36L
                1043 82ef  M3A78-EH Motherboard
                105b 0e13  N15235/A74MX mainboard / AMD SB700
        439d  SB7x0/SB8x0/SB9x0 LPC host controller
                1019 2120  A785GM-M
+               103c 1609  ProLiant MicroServer N36L
                103c 1611  Pavilion DM1Z-3000
                1043 82ef  M3A78-EH Motherboard
                1043 8443  M5A88-V EVO
                1642 3c81  Radeon HD 8670
                1642 3c91  Radeon HD 8670
                1642 3f09  Radeon R7 350
-       6611  Oland [Radeon HD 8570 / R7 240/340 OEM]
+       6611  Oland [Radeon HD 8570 / R7 240/340 / Radeon 520 OEM]
                1028 210b  Radeon R5 240 OEM
+               1642 1869  Radeon 520 OEM
                174b 4248  Radeon R7 240 OEM
                174b a240  Radeon R7 240 OEM
                174b d340  Radeon R7 340 OEM
                17aa 3805  Radeon HD 8570M
        6664  Jet XT [Radeon R5 M240]
        6665  Jet PRO [Radeon R5 M230 / R7 M260DX / Radeon 520 Mobile]
-               17aa 1309  Radeon R7 M260DX
+               17aa 1309  Z50-75 Radeon R7 M260DX
                17aa 368f  Radeon R5 A230
        6667  Jet ULT [Radeon R5 M230]
        666f  Sun LE [Radeon HD 8550M / R5 M230]
                1462 3418  Radeon RX 580 Armor 4G OC
                1462 341e  Radeon RX 570 Armor 4G OC
                1462 8a92  Radeon RX 580
-               148c 2372  Radeon RX 480
+               148c 2372  Radeon RX 480 [Red Dragon]
                148c 2373  Radeon RX 470
                1682 9470  Radeon RX 470
                1682 9480  Radeon RX 480
                1787 a470  Radeon RX 470
                1787 a480  Radeon RX 480
                1849 5001  Phantom Gaming X RX 580 OC
+               1849 5030  Phantom Gaming D Radeon RX580 8G OC
                1da2 e353  Radeon RX 570 Pulse 4GB
                1da2 e366  Nitro+ Radeon RX 570/580/590
        67e0  Baffin [Radeon Pro WX 4170]
                1043 04a0  Radeon R9 FURY X
                174b e329  Radeon R9 FURY
        7310  Navi 10
-       731f  Navi 10 [Radeon RX 5700 / 5700 XT]
-       7340  Navi 14 [Radeon RX 5500 / 5500M]
+       7312  Navi 10 [Radeon Pro W5700]
+       731f  Navi 10 [Radeon RX 5600 OEM/5600 XT / 5700/5700 XT]
+       7340  Navi 14 [Radeon RX 5500/5500M / Pro 5500M]
+       7341  Navi 14 [Radeon Pro W5500]
+       7347  Navi 14 [Radeon Pro W5500M]
+       734f  Navi 14 [Radeon Pro W5300M]
        7833  RS350 Host Bridge
        7834  RS350 [Radeon 9100 PRO/XT IGP]
        7835  RS350M [Mobility Radeon 9000 IGP]
                1019 2120  A785GM-M
                1043 83a2  M4A785TD Motherboard
        9712  RS880M [Mobility Radeon HD 4225/4250]
+               103c 1609  ProLiant MicroServer N36L
        9713  RS880M [Mobility Radeon HD 4100]
        9714  RS880 [Radeon HD 4290]
        9715  RS880 [Radeon HD 4250]
        9830  Kabini [Radeon HD 8400 / R3 Series]
        9831  Kabini [Radeon HD 8400E]
        9832  Kabini [Radeon HD 8330]
+               1849 9832  QC5000-ITX/PH
        9833  Kabini [Radeon HD 8330E]
        9834  Kabini [Radeon HD 8210]
        9835  Kabini [Radeon HD 8310E]
        9839  Kabini [Radeon HD 8180]
        983d  Temash [Radeon HD 8250/8280G]
        9840  Kabini HDMI/DP Audio
+               1849 9840  QC5000-ITX/PH
        9850  Mullins [Radeon R3 Graphics]
        9851  Mullins [Radeon R4/R5 Graphics]
                1179 f928  Beema [Radeon R5 Graphics]
        1467  Family 17h (Models 00h-0fh) Data Fabric: Device 18h; Function 7
        1468  Zeppelin Cryptographic Coprocessor NTBCCP
        1480  Starship/Matisse Root Complex
+               1462 7c37  X570-A PRO motherboard
        1481  Starship/Matisse IOMMU
        1482  Starship/Matisse PCIe Dummy Host Bridge
        1483  Starship/Matisse GPP Bridge
        1485  Starship/Matisse Reserved SPP
        1486  Starship/Matisse Cryptographic Coprocessor PSPCPP
        1487  Starship/Matisse HD Audio Controller
+               1462 9c37  X570-A PRO motherboard
        1488  Starship Reserved SSP
        1489  Starship Reserved SSP
        148a  Starship/Matisse PCIe Dummy Function
        149a  Starship PCIe GPP Bridge [1:0]
        149b  Starship Reserved SSP
        149c  Matisse USB 3.0 Host Controller
+               1462 7c37  X570-A PRO motherboard
        1510  Family 14h Processor Root Complex
                174b 1001  PURE Fusion Mini
        1512  Family 14h Processor Root Port
        1534  Family 16h Processor Function 4
        1535  Family 16h Processor Function 5
        1536  Family 16h Processor Root Complex
+               1849 1536  QC5000-ITX/PH
        1537  Kabini/Mullins PSP-Platform Security Processor
        1538  Family 16h Processor Function 0
        1539  Kabini P2P Bridge for PCIe Ports[4:0]
        15dc  Raven/Raven2 Internal PCIe GPP Bridge 0 to Bus B
        15de  Raven/Raven2/FireFlight HD Audio Controller
        15df  Family 17h (Models 10h-1fh) Platform Security Processor
+               17aa 5124  ThinkPad E595
        15e0  Raven USB 3.1
                103c 8615  Pavilion Laptop 15-cw1xxx
+               17aa 5124  ThinkPad E595
        15e1  Raven USB 3.1
                103c 8615  Pavilion Laptop 15-cw1xxx
+               17aa 5124  ThinkPad E595
        15e2  Raven/Raven2/FireFlight/Renoir Audio Processor
+               17aa 5124  ThinkPad E595
        15e3  Family 17h (Models 10h-1fh) HD Audio Controller
                103c 8615  Pavilion Laptop 15-cw1xxx
+               17aa 5124  ThinkPad E595
        15e4  Raven/Raven2/Renoir Sensor Fusion Hub
        15e5  Raven2 USB 3.1
        15e6  Raven/Raven2/Renoir Non-Sensor Fusion Hub KMDF driver
        43c7  400 Series Chipset PCIe Port
        43c8  400 Series Chipset SATA Controller
        43d5  400 Series Chipset USB 3.1 XHCI Controller
+       57a3  Matisse PCIe GPP Bridge
+       57a4  Matisse PCIe GPP Bridge
+       57ad  Matisse Switch Upstream
        7006  AMD-751 [Irongate] System Controller
        7007  AMD-751 [Irongate] AGP Bridge
        700a  AMD-IGR4 AGP Host to PCI Bridge
        7801  FCH SATA Controller [AHCI mode]
                103c 168b  ProBook 4535s Notebook
                103c 194e  ProBook 455 G1 Notebook
+               17aa 3988  Z50-75
+               1849 7801  QC5000-ITX/PH
        7802  FCH SATA Controller [RAID mode]
        7803  FCH SATA Controller [RAID mode]
        7804  FCH SATA Controller [AHCI mode]
        7807  FCH USB OHCI Controller
                103c 194e  ProBook 455 G1 Notebook
                103c 1985  Pavilion 17-e163sg Notebook PC
+               17aa 3988  Z50-75
+               1849 7807  QC5000-ITX/PH
        7808  FCH USB EHCI Controller
                103c 194e  ProBook 455 G1 Notebook
                103c 1985  Pavilion 17-e163sg Notebook PC
+               17aa 3988  Z50-75
+               1849 7808  QC5000-ITX/PH
        7809  FCH USB OHCI Controller
                103c 194e  ProBook 455 G1 Notebook
+               17aa 3988  Z50-75
        780a  Kabini/Mullins SATA Raid/AHCI Mode (DotHill driver)
        780b  FCH SMBus Controller
                103c 194e  ProBook 455 G1 Notebook
                103c 1985  Pavilion 17-e163sg Notebook PC
+               17aa 3988  Z50-75
+               1849 780b  QC5000-ITX/PH
        780c  FCH IDE Controller
        780d  FCH Azalia Controller
                103c 194e  ProBook 455 G1 Notebook
                103c 1985  Pavilion 17-e163sg Notebook PC
                1043 8444  F2A85-M Series
+               17aa 3988  Z50-75
+               1849 8892  QC5000-ITX/PH
        780e  FCH LPC Bridge
                103c 194e  ProBook 455 G1 Notebook
                103c 1985  Pavilion 17-e163sg Notebook PC
+               17aa 3988  Z50-75
+               1849 780e  QC5000-ITX/PH
        780f  FCH PCI Bridge
        7812  FCH USB XHCI Controller
        7813  FCH SD Flash Controller
        7814  FCH USB XHCI Controller
                103c 194e  ProBook 455 G1 Notebook
                103c 1985  Pavilion 17-e163sg Notebook PC
+               17aa 3988  Z50-75
+               1849 7814  QC5000-ITX/PH
        7900  FCH SATA Controller [IDE mode]
        7901  FCH SATA Controller [AHCI mode]
                103c 8615  Pavilion Laptop 15-cw1xxx
+               1462 7c37  X570-A PRO motherboard
        7902  FCH SATA Controller [RAID mode]
        7903  FCH SATA Controller [RAID mode]
        7904  FCH SATA Controller [AHCI mode]
        7908  FCH USB EHCI Controller
        790b  FCH SMBus Controller
                103c 8615  Pavilion Laptop 15-cw1xxx
+               1462 7c37  X570-A PRO motherboard
+               17aa 5124  ThinkPad E595
        790e  FCH LPC Bridge
                103c 8615  Pavilion Laptop 15-cw1xxx
+               1462 7c37  X570-A PRO motherboard
+               17aa 5124  ThinkPad E595
        790f  FCH PCI Bridge
        7914  FCH USB XHCI Controller
        9600  RS780 Host Bridge
                1043 82f1  M3A78-EH Motherboard
        9601  RS880 Host Bridge
                1019 2120  A785GM-M
+               103c 1609  ProLiant MicroServer N36L
                1043 83a2  M4A785-M Mainboard
                1043 843e  M5A88-V EVO
        9602  RS780/RS880 PCI to PCI bridge (int gfx)
        9603  RS780 PCI to PCI bridge (ext gfx port 0)
+               103c 1609  ProLiant MicroServer N36L
        9604  RS780/RS880 PCI to PCI bridge (PCIE port 0)
        9605  RS780/RS880 PCI to PCI bridge (PCIE port 1)
        9606  RS780 PCI to PCI bridge (PCIE port 2)
+               103c 1609  ProLiant MicroServer N36L
        9607  RS780/RS880 PCI to PCI bridge (PCIE port 3)
        9608  RS780/RS880 PCI to PCI bridge (PCIE port 4)
        9609  RS780/RS880 PCI to PCI bridge (PCIE port 5)
        4031  zx2 I/O Controller
        4037  PCIe Local Bus Adapter
        9602  AMD RS780/RS880 PCI to PCI bridge (int gfx)
+               103c 1609  ProLiant MicroServer N36L
 103e  Solliday Engineering
 103f  Synopsys/Logic Modeling Group
 1040  Accelgraphics Inc.
        4802  Falcon
        4803  Hawk
        4806  CPX8216
+# MPC7410 PowerPC microprocessor and PCI host bridge
+       480b  MPC7410
        4d68  20268
        5600  SM56 PCI Modem
                1057 0300  SM56 PCI Speakerphone Modem
                1077 02e4  QLE2772 Dual Port 32GFC PCIe Gen4 x8 Adapter
                1077 02ee  QLE2870 Single Port 64GFC PCIe Gen4 x8 Adapter
                1077 02f0  QLE2770 Single Port 32GFC PCIe Gen4 x8 Adapter
+               1077 02f2  QLogic 1x32Gb QLE2770 FC HBA
+               1077 02f3  QLogic 2x32Gb QLE2772 FC HBA
                1590 02d3  SN1610Q - 1P Enhanced 32GFC Single Port Fibre Channel Host Bus Adapter
                1590 02d4  SN1610Q – 2P Enhanced 32GFC Dual Port Fibre Channel Host Bus Adapter
        2300  QLA2300 64-bit Fibre Channel Adapter
                1077 0055  QLogic 2x10GE QL41132HQCU NIC
                1077 0056  2x10GE QL41132HxRJ NIC
                1077 0057  2x25GE QL41232HxCU NIC
+               1077 0065  QLogic 4x10GE QL41154HQRJ CNA
+               1077 0066  QLogic 4x10GE QL41154HQCU CNA
+               1077 0068  10GbE 2p SFP+ QL41132HLCU-HC Adapter
+               1077 0069  10GbE 2p BASE-T QL41132HQRJ-HC OCP3 Adapter
+               1077 0070  10GbE 2p BASE-T QL41132HLRJ-HC Adapter
+               1077 0071  10GbE 2p SFP+ QL41132HQCU-HC OCP3 Adapter
+               1077 0072  10GbE 4p SFP+ QL41134HLCU-HC Adapter
+               1077 0073  10/25GbE 2p SFP28 QL41232HQCU-HC OCP3 Adapter
+               1077 0074  10/25GbE 2p SFP28 QL41232HLCU-HC Adapter
                1590 021a  10GbE 2P QL41162HLRJ-HP Adapter
                1590 021b  10GbE 2P QL41162HLRJ-HP Adapter
                1590 021d  10/25GbE 2P QL41222HLCU-HP Adapter
                1077 000d  FastLinQ QL41262H 25GbE iSCSI Adapter
                1077 000e  FastLinQ QL41162H 10GbE iSCSI Adapter
                1077 000f  2x25GE QL41262HMKR CNA
+               1077 0065  QLogic 4x10GE QL41154HQRJ CNA
+               1077 0066  QLogic 4x10GE QL41154HQCU CNA
                1590 021a  10GbE 2P QL41162HLRJ-HP Adapter
                1590 021b  10GbE 2P QL41162HLRJ-HP Adapter
        8090  FastLinQ QL41000 Series Gigabit Ethernet Controller (SR-IOV VF)
                1077 0055  QLogic 2x10GE QL41132HQCU NIC
                1077 0056  2x10GE QL41132HxRJ NIC
                1077 0057  2x25GE QL41232HxCU NIC
+               1077 0065  QLogic 4x10GE QL41154HQRJ CNA
+               1077 0066  QLogic 4x10GE QL41154HQCU CNA
                1590 021a  10GbE 2P QL41162HLRJ-HP Adapter
                1590 021b  10GbE 2P QL41162HLRJ-HP Adapter
                1590 021e  10/25GbE 2P QL41162HMRJ-HP Adapter
        0fb9  GP107GL High Definition Audio Controller
        0fba  GM206 High Definition Audio Controller
        0fbb  GM204 High Definition Audio Controller
+       0fbc  GM107 High Definition Audio Controller [GeForce 940MX]
        0fc0  GK107 [GeForce GT 640 OEM]
        0fc1  GK107 [GeForce GT 640]
        0fc2  GK107 [GeForce GT 630 OEM]
        10f0  GP104 High Definition Audio Controller
        10f1  GP106 High Definition Audio Controller
        10f7  TU102 High Definition Audio Controller
+       10f8  TU104 HD Audio Controller
        10f9  TU106 High Definition Audio Controller
                1043 8673  TURBO-RTX2070-8G
        1140  GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M]
        1406  GM206 [GeForce GTX 960 OEM]
        1407  GM206 [GeForce GTX 750 v2]
        1427  GM206M [GeForce GTX 965M]
+               103c 825b  OMEN-17-w001nv
        1430  GM206GL [Quadro M2000]
        1431  GM206GL [Tesla M4]
        1436  GM206GLM [Quadro M2200 Mobile]
        174e  GM108M [GeForce MX110]
        1789  GM107GL [GRID M3-3020]
        179c  GM107 [GeForce 940MX]
+               1025 1094  Acer Aspire E5-575G
        17c2  GM200 [GeForce GTX TITAN X]
        17c8  GM200 [GeForce GTX 980 Ti]
        17f0  GM200GL [Quadro M6000]
        1adb  TU106 USB Type-C UCSI Controller
                1043 8673  TURBO-RTX2070-8G
        1aeb  TU116 High Definition Audio Controller
+       1aed  TU116 [GeForce GTX 1650 SUPER]
        1b00  GP102 [TITAN X]
        1b01  GP102 [GeForce GTX 1080 Ti 10GB]
        1b02  GP102 [TITAN Xp]
                1414 0020  GTX 1060 Mobile
        1c2d  GP106M
        1c30  GP106GL [Quadro P2000]
+       1c31  GP106GL [Quadro P2200]
        1c35  GP106
        1c60  GP106BM [GeForce GTX 1060 Mobile 6GB]
                103c 8390  GeForce GTX 1060 Max-Q 6GB
        1c70  GP106GL
        1c81  GP107 [GeForce GTX 1050]
        1c82  GP107 [GeForce GTX 1050 Ti]
+               1043 8613  PH-GTX1050TI-4G
+               1458 3763  GV-N105TOC-4GD
        1c83  GP107 [GeForce GTX 1050 3GB]
        1c8c  GP107M [GeForce GTX 1050 Ti Mobile]
        1c8d  GP107M [GeForce GTX 1050 Mobile]
        1c90  GP107M [GeForce MX150]
        1c91  GP107M [GeForce GTX 1050 3 GB Max-Q]
        1c92  GP107M [GeForce GTX 1050 Mobile]
+       1c94  GP107M [GeForce MX350]
        1ca7  GP107GL
        1ca8  GP107GL
        1caa  GP107GL
                103c 842f  P1000 [Zbook 17 G5 mobile workstation]
                103c 8451  P1000 [Zbook Studio x360 G5 mobile workstation]
        1cbc  GP107GLM [Quadro P600 Mobile]
+       1cbd  GP107GLM [Quadro P620]
        1ccc  GP107BM [GeForce GTX 1050 Ti Mobile]
        1ccd  GP107BM [GeForce GTX 1050 Mobile]
+       1cfa  GP107GL [Quadro P2000]
+       1cfb  GP107GL [Quadro P1000]
        1d01  GP108 [GeForce GT 1030]
        1d10  GP108M [GeForce MX150]
                17aa 225e  ThinkPad T480
        1d12  GP108M [GeForce MX150]
                1d72 1701  Mi Notebook Pro [GeForce MX150]
        1d13  GP108M [GeForce MX250]
+       1d16  GP108M [GeForce MX330]
        1d33  GP108GLM [Quadro P500 Mobile]
+       1d34  GP108GLM [Quadro P520]
        1d52  GP108BM [GeForce MX250]
        1d81  GV100 [TITAN V]
        1db1  GV100GL [Tesla V100 SXM2 16GB]
-       1db2  GV100GL [Tesla V100-DGXS-16GB]
+       1db2  GV100GL [Tesla V100 DGXS 16GB]
        1db3  GV100GL [Tesla V100 FHHL 16GB]
        1db4  GV100GL [Tesla V100 PCIe 16GB]
        1db5  GV100GL [Tesla V100 SXM2 32GB]
        1db6  GV100GL [Tesla V100 PCIe 32GB]
        1db7  GV100GL [Tesla V100 DGXS 32GB]
+       1db8  GV100GL [Tesla V100 SXM3 32GB]
+               10de 131d  Tesla V100-SXM3-32GB-H
        1dba  GV100GL [Quadro GV100]
                10de 12eb  TITAN V CEO Edition
+       1df0  GV100GL [Tesla PG500-216]
+       1df2  GV100GL [Tesla PG503-216]
+       1df5  GV100GL [Tesla V100 SXM2 16GB]
+       1df6  GV100GL [Tesla V100S PCIe 32GB]
        1e02  TU102 [TITAN RTX]
        1e04  TU102 [GeForce RTX 2080 Ti]
        1e07  TU102 [GeForce RTX 2080 Ti Rev. A]
        1e30  TU102GL [Quadro RTX 6000/8000]
                10de 129e  Quadro RTX 8000
                10de 12ba  Quadro RTX 6000
+       1e37  TU102GL [GRID RTX T10-4/T10-8/T10-16]
+               10de 1347  GRID RTX T10-8
+               10de 1348  GRID RTX T10-4
+               10de 1370  GRID RTX T10-16
        1e38  TU102GL
        1e3c  TU102GL
        1e3d  TU102GL
        1e3e  TU102GL
+       1e78  TU102GL [Quadro RTX 6000/8000]
+               10de 13d8  Quadro RTX 8000
+               10de 13d9  Quadro RTX 6000
        1e81  TU104 [GeForce RTX 2080 SUPER]
        1e82  TU104 [GeForce RTX 2080]
        1e84  TU104 [GeForce RTX 2070 SUPER]
        1e87  TU104 [GeForce RTX 2080 Rev. A]
+       1e89  TU104 [GeForce RTX 2060]
        1e90  TU104M [GeForce RTX 2080 Mobile]
        1eab  TU104M
        1eae  TU104M
        1eb8  TU104GL [Tesla T4]
        1eb9  TU104GL
        1ebe  TU104GL
+       1ec2  TU104 [GeForce RTX 2070 SUPER]
+       1ec7  TU104 [GeForce RTX 2070 SUPER]
        1ed0  TU104BM [GeForce RTX 2080 Mobile]
        1f02  TU106 [GeForce RTX 2070]
                1043 8673  TURBO RTX 2070
        1f11  TU106M [GeForce RTX 2060 Mobile]
        1f2e  TU106M
        1f36  TU106GLM [Quadro RTX 3000 Mobile / Max-Q]
+       1f42  TU106 [GeForce RTX 2060 SUPER]
+       1f47  TU106 [GeForce RTX 2060 SUPER]
        1f50  TU106BM [GeForce RTX 2070 Mobile]
        1f51  TU106BM [GeForce RTX 2060 Mobile]
        1f81  TU117
        1f82  TU117 [GeForce GTX 1650]
+       1f91  TU117M [GeForce GTX 1650 Mobile / Max-Q]
        1f92  TU117M [GeForce GTX 1650 Mobile]
+       1f96  TU117M [GeForce GTX 1650 Mobile / Max-Q]
        1fae  TU117GL
        1fb8  TU117GLM [Quadro T2000 Mobile / Max-Q]
        1fb9  TU117GLM [Quadro T1000 Mobile]
        2182  TU116 [GeForce GTX 1660 Ti]
        2183  TU116
        2184  TU116 [GeForce GTX 1660]
+       2187  TU116 [GeForce GTX 1650 SUPER]
        2191  TU116M [GeForce GTX 1660 Ti Mobile]
        21ae  TU116GL
        21bf  TU116GL
+       21c4  TU116 [GeForce GTX 1660 SUPER]
        21d1  TU116BM [GeForce GTX 1660 Ti Mobile]
 10df  Emulex Corporation
        0720  OneConnect NIC (Skyhawk)
                17aa 3832  Yoga 520
        522a  RTS522A PCI Express Card Reader
                103c 8079  EliteBook 840 G3
+               103c 825b  OMEN-17-w001nv
+               17aa 5124  ThinkPad E595
        5249  RTS5249 PCI Express Card Reader
                103c 1909  ZBook 15
        524a  RTS524A PCI Express Card Reader
        5260  RTS5260 PCI Express Card Reader
        5286  RTS5286 PCI Express Card Reader
        5287  RTL8411B PCI Express Card Reader
+               1025 1094  Acer Aspire E5-575G
        5288  RTS5288 PCI Express Card Reader
        5289  RTL8411 PCI Express Card Reader
                1043 1457  K55A Laptop
                103c 006a  NX9500
                103c 2a20  Pavilion t3030.de Desktop PC
                103c 30d9  Presario C700
-               1043 1045  L8400B or L3C/S notebook
+               1043 1045  L8400B, L3C/S, X58LE notebook
                1043 8109  P5P800-MX Mainboard
                1071 8160  MIM2000
                10bd 0320  EP-320X-R
                1462 236c  945P Neo3-F motherboard
        8168  RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller
                1019 8168  RTL8111/8168 PCI Express Gigabit Ethernet controller
+               1025 1094  Acer Aspire E5-575G
                1028 0283  Vostro 220
                1028 04b2  Vostro 3350
                1028 04da  Vostro 3750
                103c 1611  Pavilion DM1Z-3000
                103c 1950  ProBook 450/455
                103c 2a6f  Asus IPIBL-LB Motherboard
+               103c 825b  OMEN-17-w001nv
                103c 8615  Pavilion Laptop 15-cw1xxx
                1043 11f5  Notebook motherboard (one of many models)
                1043 16d5  U6V/U31J laptop
                1462 368c  K9AG Neo2
                1462 4180  Wind PC MS-7418
                1462 7522  X58 Pro-E
+               1462 7c37  X570-A PRO motherboard
                1775 11cc  CC11/CL11
+               17aa 3814  Z50-75
+               17aa 5124  ThinkPad E595
                1849 8168  Motherboard (one of many)
                7470 3468  TG-3468 Gigabit PCI Express Network Adapter
                8086 2055  NUC Kit DN2820FYKH
        8821  RTL8821AE 802.11ac PCIe Wireless Network Adapter
        b723  RTL8723BE PCIe Wireless Network Adapter
                10ec 8739  Dell Wireless 1801
+               17aa b736  Z50-75
        b822  RTL8822BE 802.11a/b/g/n/ac WiFi adapter
                103c 831b  Realtek RTL8822BE 802.11ac 2 × 2 Wi-Fi + Bluetooth 4.2 Combo Adapter (MU-MIMO supported)
+               17aa 5124  ThinkPad E595
+               17aa b023  ThinkPad E595
        c821  RTL8821CE 802.11ac PCIe Wireless Network Adapter
+       c822  RTL8822CE 802.11ac PCIe Wireless Network Adapter
        d723  RTL8723DE 802.11b/g/n PCIe Adapter
 10ed  Ascii Corporation
        7310  V7310
                1102 0021  X-Fi Platinum
                1102 002c  X-Fi XtremeGamer FATAL1TY PRO
                1102 1003  X-Fi XtremeMusic
-       0006  EMU10k1X [SB Live! Value/OEM Series]
+# This chip is also known as CA0103 on Sound Blaster 5.1 SB0680 card.
+       0006  EMU10k1X / CA0103 [SB Live! OEM / SB 5.1 / Ectiva 5.1]
+               1102 1001  SB0680 Sound Blaster 5.1
+               1102 1003  SB0203 SB Live! 5.1 (Dell)
+               1102 1004  TP0033 Ectiva Audio 5.1
        0007  CA0106/CA0111 [SB Live!/Audigy/X-Fi Series]
                1102 0007  SBLive! 24bit
                1102 1001  SB0310 Audigy LS
                1259 2975  AT-2970SX/2SC Gigabit Ethernet Adapter
                1259 2976  AT-2970LX/2SC Gigabit Ethernet Adapter
                1259 2977  AT-2970TX/2TX Gigabit Ethernet Adapter
-       4320  SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter, PCI64, Fiber ZX/SC
+       4320  SK-98xx V2.0 Gigabit Ethernet Adapter [Marvell 88E8001]
                1148 0121  Marvell RDK-8001 Adapter
                1148 0221  Marvell RDK-8002 Adapter
                1148 0321  Marvell RDK-8003 Adapter
                1148 5061  SK-9861 V2.0 Gigabit Ethernet 1000Base-SX Adapter
                1148 5071  SK-9871 V2.0 Gigabit Ethernet 1000Base-ZX Adapter
                1148 9521  SK-9521 10/100/1000Base-T Adapter
+               1259 2916  AT-2916T
        4400  SK-9Dxx Gigabit Ethernet Adapter
        4500  SK-9Mxx Gigabit Ethernet Adapter
-       9000  SK-9S21 10/100/1000Base-T Server Adapter, PCI-X, Copper RJ-45
+       9000  SK-9Sxx Gigabit Ethernet Server Adapter PCI-X [Marvell 88E8022]
+               1148 2100  SK-9S21 10/100/1000Base-T Server Adapter, PCI-X, Copper RJ-45
+               1148 2200  SK-9S22 10/100/1000Base-T Dual Port Server Adapter, PCI-X, 2 Copper RJ-45
+               1148 2210  SK-9P22 10/100/1000 Base-T Dual Port PMC card
+               1148 2220  TPMC-GBE-CO
+               1148 8100  SK-9S81 1000Base-SX Server Adapter,PCI-X, Fiber SX/LC
+               1148 8200  SK-9S82 1000Base-SX Dual Port Server Adapter, PCI-X, 2 Fiber SX/LC
+               1148 8210  SK-9P82 1000 Base-SX Dual Port PMC card
+               1148 8220  TPMC-GBE-FI
+               1148 9100  SK-9S91 1000Base-LX Server Adapter,PCI-X, Fiber LX/LC
+               1148 9200  SK-9S92 1000Base-LX Dual Port Server Adapter, PCI-X, 2 Fiber LX/LC
+               1259 2973  AT-2971SX v2 Gigabit Adapter
+               1259 2974  AT-2971T v2 Gigabit Adapter
+               1259 2978  AT-2971LX Gigabit Adapter
        9843  [Fujitsu] Gigabit Ethernet
        9e00  SK-9E21D 10/100/1000Base-T Adapter, Copper RJ-45
                1148 2100  SK-9E21 Server Adapter
                1028 0188  Inspiron 6000 laptop
                103c 30c0  Compaq 6710b
                103c 30c1  Compaq 6910p
+               1043 1017  X58LE
                1043 1237  A6J-Q008
                1043 1967  V6800V
                1043 1987  A4K and Z81K notebooks, possibly others ( mid-2005 machines )
                103c 30b7  Presario V6133CL
                103c 30cc  Pavilion dv6700
                103c 30cf  Pavilion dv95xx/96xx/97xx/98xx series
+               1043 1017  X58LE
                1043 1237  A6J-Q008
                1043 1967  V6800V
                104d 9035  VAIO VGN-FW11ZRU
                103c 30c1  Compaq 6910p
                103c 30cc  Pavilion dv6700
                103c 30cf  Pavilion dv9668eg Laptop
+               1043 1017  X58LE
                1043 1237  A6J-Q008
                1043 1967  V6800V
                10f7 8338  Panasonic CF-Y5 laptop
                103c 1521  HP EliteBook 8540w
                103c 30b7  Presario V6133CL
                103c 30cf  Pavilion dv9500/9600/9700 series
+               1043 1017  X58LE
                1183 0843  Alienware Aurora m9700
        0852  xD-Picture Card Controller
                1025 0121  Aspire 5920G
        4380  88E8057 PCI-E Gigabit Ethernet Controller
 # AVB = "Audio Video Bridging"
        4381  Yukon Optima 88E8059 [PCIe Gigabit Ethernet Controller with AVB]
+               1259 2803  AT-2814FX
+               1259 2804  AT-2874xx
        4611  GT-64115 System Controller
        4620  GT-64120/64120A/64121A System Controller
        4801  GT-48001
        000e  PM/PPC
 1224  Interactive Images
 1225  Power I/O, Inc.
-1227  Tech-Source
+1227  EIZO Rugged Solutions
        0006  Raptor GFX 8P
        0023  Raptor GFX [1100T]
        0045  Raptor 4000-L [Linux version]
                1028 1ff8  Express Flash PM1725b 3.2TB AIC
                1028 1ff9  Express Flash PM1725b 6.4TB AIC
                1028 1ffa  Express Flash PM1725b 12.8TB AIC
+       a824  NVMe SSD Controller PM173X
 144e  OLITEC
 144f  Askey Computer Corp.
 1450  Octave Communications Ind.
                10cf 1279  LifeBook E8010D
        165f  NetXtreme BCM5720 2-port Gigabit Ethernet PCIe
                1028 04f7  PowerEdge R320 server
+               1028 08fd  PowerEdge R6515/R7515 LOM
                1028 08ff  PowerEdge Rx5xx LOM Board
                1028 0900  PowerEdge C6525 LOM
                103c 1786  NC332T Adapter
        4430  BCM44xx CardBus iLine32 HomePNA 2.0
        4432  BCM4432 CardBus 10/100BaseT
        4464  BCM4364 802.11ac Wireless Network Adapter
+# brcmfmac reports it as BCM4377/4 but macOS drivers call it BCM4377b
+       4488  BCM4377b Wireless Network Adapter
        4610  BCM4610 Sentry5 PCI to SB Bridge
        4611  BCM4610 Sentry5 iLine32 HomePNA 1.0
        4612  BCM4610 Sentry5 V.90 56k Modem
        0263  MT27710 [ConnectX-4 Lx Programmable Virtual Function] EN
        0264  Innova-2 Flex Burn image
        0281  NPS-600 Flash Recovery
+       0538  MT2910 Family [ConnectX-7 Flash Recovery]
+       0539  MT2910 Family [ConnectX-7 Secure Flash Recovery]
        1002  MT25400 Family [ConnectX-2 Virtual Function]
        1003  MT27500 Family [ConnectX-3]
                1014 04b5  PCIe3 40GbE RoCE Converged Host Bus Adapter for Power
        101e  ConnectX Family mlx5Gen Virtual Function
        101f  MT2894 Family [ConnectX-6 Lx]
        1020  MT28860
-       1021  MT28861
+       1021  MT2910 Family [ConnectX-7]
        1974  MT28800 Family [ConnectX-5 PCIe Bridge]
        1975  MT416842 Family [BlueField SoC PCIe Bridge]
        1976  MT28908 Family [ConnectX-6 PCIe Bridge]
        1978  MT42822 Family [BlueField-2 SoC PCIe Bridge]
        4117  MT27712A0-FDCF-AE
                1bd4 0039  SN10XMP2P25
+               1bd4 003a  25G SFP28 SP EO251FM9 Adapter
                1bd4 004d  SN10XMP2P25,YZPC-01191-101
        5274  MT21108 InfiniBridge
        5a44  MT23108 InfiniHost
        0040  QCA9980/9990 802.11ac Wireless Network Adapter
        0041  QCA6164 802.11ac Wireless Network Adapter
        0042  QCA9377 802.11ac Wireless Network Adapter
+               11ad 08a6  Qualcomm Atheros QCA9377 802.11ac Wireless Network Adapter
        0046  QCA9984 802.11ac Wave 2 Wireless Network Adapter
        0050  QCA9887 802.11ac Wireless Network Adapter
        0207  AR5210 Wireless Network Adapter [AR5000 802.11a]
                17d3 1882  ARC-1882 8/12/16/24 Port PCIe 3.0 to SAS/SATA 6Gb RAID Controller
                17d3 1883  ARC-1883 8/12/16/24 Port PCIe 3.0 to SAS/SATA 12Gb RAID Controller
        1884  ARC-1884 series PCIe 3.0 to SAS/SATA 12/6Gb RAID Controller
+       188a  ARC-1886 series PCIe 4.0 to NVMe/SAS/SATA 16/12/6Gb RAID Controller
 # nee Neterion Inc., previously S2io Inc.
 17d5  Exar Corp.
        5731  Xframe 10-Gigabit Ethernet PCI-X
        1026  AR8121/AR8113/AR8114 Gigabit or Fast Ethernet
                1043 8304  P5KPL-CM Motherboard
        1048  Attansic L1 Gigabit Ethernet
-               1043 8226  P5KPL-VM Motherboard
+               1043 8226  P5B-MX/WiFi-AP, P5KPL-VM Motherboard
        1062  AR8132 Fast Ethernet
        1063  AR8131 Gigabit Ethernet
                1458 e000  GA-G31M-ES2L Motherboard
 1987  Phison Electronics Corporation
        5007  E7 NVMe Controller
        5012  E12 NVMe Controller
+       5016  E16 PCIe4 NVMe Controller
 1989  Montilio Inc.
        0001  RapidFile Bridge
        8001  RapidFile
                19e5 3034  NVMe SSD ES3600C V3 1600GB HHHL AIC
                19e5 3036  NVMe SSD ES3600C V3 3200GB HHHL AIC
        0200  Hi1822 Family (2*100GE)
+               19e5 d139  Hi1822 SP572 (2*100GE)
+               19e5 d13d  Hi1822 SC371 (2*100GE)
        0202  Hi1822 Family (2*32G FC)
+               19e5 d302  Hi1822 SP521 (2*32G FC)
+               19e5 d304  Hi1822 SP526 (2*32G FC)
        0203  Hi1822 Family (2*16G FC)
+               19e5 d301  Hi1822 SP520 (2*16G FC)
+               19e5 d305  Hi1822 SP525 (2*16G FC)
        0205  Hi1822 Family (2*100GE)
+               19e5 df27  Hi1822 MZ731 MEZZ (2*100GE)
        0206  Hi1822 Family (2*25GE)
+               19e5 d138  Hi1822 SP582 (2*25GE)
+               19e5 d13a  Hi1822 SC381 (2*25GE)
        0210  Hi1822 Family (4*25GE)
+               19e5 df2e  Hi1822 MZ532 MEZZ (4*25GE)
        0211  Hi1822 Family (4*25GE)
+               19e5 d12f  Hi1822 SP571 (4*25GE)
+               19e5 d137  Hi1822 SP581 (4*25GE)
+               19e5 d142  Hi1822 SP583 (4*25GE)
        0212  Hi1822 Family (2*8G FC)
+               19e5 d303  Hi1822 SP522 (2*8G FC)
+               19e5 d306  Hi1822 SP523 (2*8G FC)
        1710  iBMA Virtual Network Adapter
        1711  Hi1710 [iBMC Intelligent Management system chip w/VGA support]
        1822  Hi1822 Family (4*25GE)
+               19e5 d129  Hi1822 SP570 (4*25GE)
+               19e5 d136  Hi1822 SP580 (4*25GE)
+               19e5 d141  Hi1822 SP583 (4*25GE)
        371e  Hi1822 Family Virtual Bridge
        375e  Hi1822 Family Virtual Function
        379e  Hi1822 Family Virtual Function
        4005  Accelerated Virtual Video Adapter
        4006  Memory Ballooning Controller
 1ab9  Espia Srl
+1ac1  Global Unichip Corp.
+       089a  Coral Edge TPU
 1ac8  Aeroflex Gaisler
 1acc  Point of View BV
 1ad7  Spectracom Corporation
        0310  Wil6200 802.11ad Wireless Network Adapter
 1aea  Alcor Micro
        6601  AU6601 PCI-E Flash card reader controller
+       6621  AU6621 PCI-E Flash card reader controller
+       6625  AU6625 PCI-E Flash card reader controller
 1aec  Wolfson Microelectronics
 # nee Fusion-io
 1aed  SanDisk
 1b6f  Etron Technology, Inc.
        7023  EJ168 USB 3.0 Host Controller
        7052  EJ188/EJ198 USB 3.0 Host Controller
+               1849 7052  QC5000-ITX/PH
 1b73  Fresco Logic
        1000  FL1000G USB 3.0 Host Controller
                1d5c 1000  Anker USB 3.0 Express Card
        00a4  FBC4XGG3 Capture 4x10Gb [Livigno]
        00a5  FBC2XLG Capture 2x40Gb [Livorno]
        00a6  FBC1CG Capture 1x100Gb
-       00a9  FBC2XGHH Capture 2x10Gb
+       00a9  FBC2XGHH Capture 2x10Gb [Latina]
        00ad  FBC2CGG3HL Capture 2x100Gb [Padua]
        00af  Capture slave device
        00e0  PacketMover 2x100Gb [Savona]
        1283  PC300 NVMe Solid State Drive 256GB
        1284  PC300 NVMe Solid State Drive 512GB
        1285  PC300 NVMe Solid State Drive 1TB
+       1327  BC501 NVMe Solid State Drive 512GB
        1504  SC300 512GB M.2 2280 SATA Solid State Drive
 1c5f  Beijing Memblaze Technology Co. Ltd.
+       000d  PBlaze5 520/526 AIC
+       003d  PBlaze5 920/926 AIC
+       010d  PBlaze5 520/526 U.2
+       013d  PBlaze5 920/926 U.2
        0540  PBlaze4 NVMe SSD
+       0550  PBlaze5 700/900
+       0555  PBlaze5 510/516
+       0557  PBlaze5 910/916
 # http://www.nicevt.ru/ (in Russian)
 1c63  Science and Research Centre of Computer Technology (JSC "NICEVT")
 # http://www.radiotec.ru/catalog.php?cat=jr8&art=14109
 1dbb  NGD Systems, Inc.
 1dbf  Guizhou Huaxintong Semiconductor Technology Co., Ltd
        0401  StarDragon4800 PCI Express Root Port
+1dc5  FADU Inc.
+1dcd  Liqid Inc.
 1dd8  Pensando Systems Inc
        1000  DSC Capri Upstream Port
                1dd8 4000  Naples 100Gb 2-port QSFP28 x16 8GB
                1df3 0001  ENA2080F
                1df3 0002  ENA2080FS
                1df3 0003  ENA2100F
+               1df3 0004  ENA2040F
        0204  ACE-NIC-NID Programmable Network Accelerator
                1df3 0001  ENA1020Z
                1df3 0002  ENA1020ZS
 # JungleCat VU35P Module
        1635  JCM35
 1e26  Fujitsu Client Computing Limited
-1e38  Thinci, Inc
+1e36  Shanghai Enflame Technology Co. Ltd
+       0001  T10 [CloudBlazer]
+# nee Thinci, Inc
+1e38  Blaize, Inc
 1e3d  Burlywood, Inc
 1e49  Yangtze Memory Technologies Co.,Ltd
 1e4c  GSI Technology
        0010  Gemini [ Lida ]
                1e4c 0120  SE120
 1e57  Beijing Panyi Technology Co., Ltd
+       0100  The device has already been deleted.
+               0000 0100  PY8800 64GB Accelerator
 1e6b  Axiado Corp.
+1e89  ID Quantique SA
+       0002  Quantis-PCIe-40M
+       0003  Quantis-PCIe-240M
+# aka SED Systems
+1e94  Calian SED
 # nee Tumsan Oy
 1fc0  Ascom (Finland) Oy
        0300  E2200 Dual E1/Rawpipe Card
                1043 108d  VivoBook X202EV
                1043 1477  N56VZ
                1043 1517  Zenbook Prime UX31A
+               10cf 16bf  LIFEBOOK E752
        0155  Xeon E3-1200 v2/3rd Gen Core processor PCI Express Root Port
                8086 2010  Server Board S1200BTS
        0156  3rd Gen Core processor Graphics Controller
        0166  3rd Gen Core processor Graphics Controller
                1043 1517  Zenbook Prime UX31A
                1043 2103  N56VZ
+               10cf 16c1  LIFEBOOK E752
        016a  Xeon E3-1200 v2/3rd Gen Core processor Graphics Controller
                1043 844d  P8B WS Motherboard
        0172  Xeon E3-1200 v2/3rd Gen Core processor Graphics Controller
                1014 0549  Thinkpad
                1179 0001  PRO/1000 MT Mobile Connection
                8086 101e  PRO/1000 MT Mobile Connection
+       101f  Ethernet Controller V710 for 5GBASE-T
        1026  82545GM Gigabit Ethernet Controller
                1028 0168  Precision Workstation 670 Mainboard
                1028 0169  Precision 470
        104e  Ethernet Controller X710 for 10 Gigabit SFP+
        104f  Ethernet Controller X710 for 10 Gigabit backplane
        1050  82562EZ 10/100 Ethernet Controller
+               1014 0287  ThinkCentre S50
                1028 019d  Dimension 3000
                1462 728c  865PE Neo2 (MS-6728)
                1462 758c  MS-6758 (875P Neo)
                8086 357a  Server Board S1200BTS
        1503  82579V Gigabit Network Connection
                1043 849c  P8P67 Deluxe Motherboard
+               10cf 161c  LIFEBOOK E752
        1507  Ethernet Express Module X520-P2
        1508  82598EB Gigabit BX Network Connection
        1509  82580 Gigabit Network Connection
                108e 7b15  Sun Dual Port 10 GbE PCIe 2.0 Low Profile Adapter, Base-T
                1137 00bf  Ethernet Converged Network Adapter X540-T2
                1170 0052  Ethernet Controller 10-Gigabit X540-AT2
+               15d9 0734  AOC-STG-I2T
                17aa 1073  ThinkServer X540-T2 AnyFabric
                17aa 4006  Ethernet Controller 10-Gigabit X540-AT2
                1bd4 001a  10G base-T DP ER102Ti3 Rack Adapter
                18d4 0c08  X550 10Gb 2-port RJ45 OCP Mezz Card MOP81-I-10GT2
                193d 1008  560T-B
                193d 1009  560T-L
+               193d 1011  UN-NIC-ETH563T-sL-2P
                8086 0001  Ethernet Converged Network Adapter X550-T2
                8086 001a  Ethernet Converged Network Adapter X550-T2
                8086 001b  Ethernet Server Adapter X550-T2 for OCP
                1137 0000  Ethernet Network Adapter XXV710
                1137 0225  Ethernet Network Adapter XXV710
                1137 02b4  Ethernet Network Adapter XXV710 OCP 2.0
+# UEFI PXE Disabled
+               1374 0230  Single Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G1I71)
+# With UEFI PXE Enabled
+               1374 0231  Single Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G1I71EU)
+# UEFI PXE Disabled
+               1374 0234  Dual Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G2I71)
+# With UEFI PXE Enabled
+               1374 0235  Dual Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G2I71EU)
+# PCIe x8 Bifurcated as x4x4, UEFI PXE Disabled, low profile
+               1374 0238  Quad Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G4I71L)
+# PCIe x8 Bifurcated as x4x4, UEFI PXE Enabled, low profile
+               1374 0239  Quad Port 25 Gigabit Ethernet PCI Express Server Adapter (PE325G4I71LEU)
+# PCIe x16 Bifurcated as x8x8, UEFI PXE Disabled, low profile
+               1374 023a  Quad Port 25 Gigabit Ethernet PCI Express Server Adapter (PE31625G4I71L)
+# PCIe x16 Bifurcated as x8x8, UEFI PXE Enabled, low profile
+               1374 023b  Quad Port 25 Gigabit Ethernet PCI Express Server Adapter (PE31625G4I71LEU)
                1590 0000  Ethernet Network Adapter XXV710-2
                1590 0253  Ethernet 10/25/Gb 2-port 661SFP28 Adapter
                8086 0000  Ethernet Network Adapter XXV710
        15ec  JHL7540 Thunderbolt 3 USB Controller [Titan Ridge 4C 2018]
        15ef  JHL7540 Thunderbolt 3 Bridge [Titan Ridge DD 2018]
        15f0  JHL7540 Thunderbolt 3 USB Controller [Titan Ridge DD 2018]
+       15f4  Ethernet Connection (15) I219-LM
+       15f5  Ethernet Connection (15) I219-V
        15f6  I210 Gigabit Ethernet Connection
+       15f9  Ethernet Connection (14) I219-LM
+       15fa  Ethernet Connection (14) I219-V
+       15fb  Ethernet Connection (13) I219-LM
+       15fc  Ethernet Connection (13) I219-V
        15ff  Ethernet Controller X710 for 10GBASE-T
                1137 0000  X710TLG GbE RJ45 PCIe NIC
                1137 02c1  X710T2LG 2x10 GbE RJ45 PCIe NIC
        1903  Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Thermal Subsystem
                1028 06dc  Latitude E7470
                1028 06e4  XPS 15 9550
+               103c 825b  OMEN-17-w001nv
                17aa 225d  ThinkPad T480
        1904  Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers
                1028 06dc  Latitude E7470
        190f  Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers
        1910  Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers
                1028 06e4  XPS 15 9550
+               103c 825b  OMEN-17-w001nv
        1911  Xeon E3-1200 v5/v6 / E3-1500 v5 / 6th/7th/8th Gen Core Processor Gaussian Mixture Model
                17aa 2247  ThinkPad T570
                17aa 224f  ThinkPad X1 Carbon 5th Gen
        1919  Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Imaging Unit
        191b  HD Graphics 530
                1028 06e4  XPS 15 9550
+               103c 825b  OMEN-17-w001nv
        191d  HD Graphics P530
        191e  HD Graphics 515
        191f  Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers
        19df  Atom Processor C3000 Series SMBus controller
        19e0  Atom Processor C3000 Series SPI Controller
        19e2  Atom Processor C3000 Series QuickAssist Technology
+       1a1c  Ethernet Connection (17) I219-LM
+       1a1d  Ethernet Connection (17) I219-V
+       1a1e  Ethernet Connection (16) I219-LM
+       1a1f  Ethernet Connection (16) I219-V
        1a21  82840 840 [Carmel] Chipset Host Bridge (Hub A)
        1a23  82840 840 [Carmel] Chipset AGP Bridge
        1a24  82840 840 [Carmel] Chipset PCI Bridge (Hub B)
                1043 108d  VivoBook X202EV
                1043 1477  N56VZ
                1043 1517  Zenbook Prime UX31A
+               10cf 16e2  LIFEBOOK E752
                144d c652  NP300E5C series laptop
        1e04  7 Series/C210 Series Chipset Family SATA Controller [RAID mode]
        1e05  7 Series Chipset SATA Controller [RAID mode]
                1043 1477  N56VZ
                1043 1517  Zenbook Prime UX31A
                1043 84ca  P8H77-I Motherboard
+               10cf 16e9  LIFEBOOK E752
                144d c652  NP300E5C series laptop
                1849 1e10  Motherboard
        1e12  7 Series/C210 Series Chipset Family PCI Express Root Port 2
                1043 1477  N56VZ
                1043 1517  Zenbook Prime UX31A
        1e14  7 Series/C210 Series Chipset Family PCI Express Root Port 3
+               10cf 16e9  LIFEBOOK E752
        1e16  7 Series/C216 Chipset Family PCI Express Root Port 4
                1043 108d  VivoBook X202EV
                1043 1477  N56VZ
                1849 1e1a  Motherboard
        1e1c  7 Series/C210 Series Chipset Family PCI Express Root Port 7
        1e1e  7 Series/C210 Series Chipset Family PCI Express Root Port 8
+               10cf 16e9  LIFEBOOK E752
                1849 1e1e  Motherboard
        1e20  7 Series/C216 Chipset Family High Definition Audio Controller
                1028 054b  XPS One 2710
                1043 1517  Zenbook Prime UX31A
                1043 8415  P8H77-I Motherboard
                1043 8445  P8Z77-V LX Motherboard
+               10cf 1757  LIFEBOOK E752
                144d c652  NP300E5C series laptop
                1849 1898  Z77 Extreme4 motherboard
        1e22  7 Series/C216 Chipset Family SMBus Controller
                1043 1477  N56VZ
                1043 1517  Zenbook Prime UX31A
                1043 84ca  P8 series motherboard
+               10cf 16e6  LIFEBOOK E752
                144d c652  NP300E5C series laptop
                1849 1e22  Motherboard
        1e24  7 Series/C210 Series Chipset Family Thermal Management Controller
                1043 1477  N56VZ
                1043 1517  Zenbook Prime UX31A
                1043 84ca  P8 series motherboard
+               10cf 16e8  LIFEBOOK E752
                144d c652  NP300E5C series laptop
                1849 1e26  Motherboard
        1e2d  7 Series/C216 Chipset Family USB Enhanced Host Controller #2
                1043 1477  N56VZ
                1043 1517  Zenbook Prime UX31A
                1043 84ca  P8 series motherboard
+               10cf 16e8  LIFEBOOK E752
                144d c652  NP300E5C series laptop
                1849 1e2d  Motherboard
        1e31  7 Series/C210 Series Chipset Family USB xHCI Host Controller
                1043 1477  N56VZ
                1043 1517  Zenbook Prime UX31A
                1043 84ca  P8 series motherboard
+               10cf 16ee  LIFEBOOK E752
                17aa 21f3  ThinkPad T430
                1849 1e31  Motherboard
        1e33  7 Series/C210 Series Chipset Family LAN Controller
                1043 1477  N56VZ
                1043 1517  Zenbook Prime UX31A
                1043 84ca  P8 series motherboard
+               10cf 16ea  LIFEBOOK E752
                144d c652  NP300E5C series laptop
                1849 1e3a  Motherboard
        1e3b  7 Series/C210 Series Chipset Family MEI Controller #2
        1e59  HM76 Express Chipset LPC Controller
                1043 1477  N56VZ
                1043 1517  Zenbook Prime UX31A
+               10cf 16e0  LIFEBOOK E752
        1e5a  7 Series Chipset Family LPC Controller
        1e5b  UM77 Express Chipset LPC Controller
        1e5c  7 Series Chipset Family LPC Controller
                103c 309f  Compaq nx9420 Notebook
                103c 30a3  Compaq nw8440
                103c 30c1  Compaq 6910p
+               1043 1017  X58LE
                104d 902d  VAIO VGN-NR120E
                105b 0d7c  D270S/D250S Motherboard
                1071 8209  Medion MIM 2240 Notebook PC [MD98100]
                8086 4c43  Desktop Board D865GLC
                8086 524c  D865PERL mainboard
        24d2  82801EB/ER (ICH5/ICH5R) USB UHCI Controller #1
+               1014 0287  ThinkCentre S50
                1014 02dd  eServer xSeries server mainboard
                1014 02ed  eServer xSeries server mainboard
                1028 0168  Precision Workstation 670 Mainboard
                8086 4c43  Desktop Board D865GLC
                8086 524c  D865PERL mainboard
        24d3  82801EB/ER (ICH5/ICH5R) SMBus Controller
+               1014 0287  ThinkCentre S50
                1014 02dd  eServer xSeries server mainboard
                1014 02ed  eServer xSeries server mainboard
                1028 0156  Precision 360
                8086 4c43  Desktop Board D865GLC
                8086 524c  D865PERL mainboard
        24d4  82801EB/ER (ICH5/ICH5R) USB UHCI Controller #2
+               1014 0287  ThinkCentre S50
                1014 02dd  eServer xSeries server mainboard
                1014 02ed  eServer xSeries server mainboard
                1028 0168  Precision Workstation 670 Mainboard
                8086 524c  D865PERL mainboard
        24d5  82801EB/ER (ICH5/ICH5R) AC'97 Audio Controller
                100a 147b  Abit IS7-E motherboard
+               1014 0287  ThinkCentre S50
                1028 0168  Precision Workstation 670 Mainboard
                1028 0169  Precision 470
                103c 006a  NX9500
        24d6  82801EB/ER (ICH5/ICH5R) AC'97 Modem Controller
                103c 006a  NX9500
        24d7  82801EB/ER (ICH5/ICH5R) USB UHCI Controller #3
+               1014 0287  ThinkCentre S50
                1014 02ed  xSeries server mainboard
                1028 0168  Precision Workstation 670 Mainboard
                1028 0169  Precision 470
                8086 4c43  Desktop Board D865GLC
                8086 524c  D865PERL mainboard
        24db  82801EB/ER (ICH5/ICH5R) IDE Controller
+               1014 0287  ThinkCentre S50
                1014 02dd  eServer xSeries server mainboard
                1014 02ed  eServer xSeries server mainboard
                1028 0168  Precision Workstation 670 Mainboard
                8086 524c  D865PERL mainboard
        24dc  82801EB (ICH5) LPC Interface Bridge
        24dd  82801EB/ER (ICH5/ICH5R) USB2 EHCI Controller
+               1014 0287  ThinkCentre S50
                1014 02dd  eServer xSeries server mainboard
                1014 02ed  eServer xSeries server mainboard
                1028 0168  Precision Workstation 670 Mainboard
                8086 4c43  Desktop Board D865GLC
                8086 524c  D865PERL mainboard
        24de  82801EB/ER (ICH5/ICH5R) USB UHCI Controller #4
+               1014 0287  ThinkCentre S50
                1014 02ed  xSeries server mainboard
                1028 0168  Precision Workstation 670 Mainboard
                1028 0169  Precision 470
                1458 2570  GA-8IPE1000 Pro2 motherboard (865PE)
        2571  82865G/PE/P AGP Bridge
        2572  82865G Integrated Graphics Controller
+               1014 0287  ThinkCentre S50
                1028 019d  Dimension 3000
                103c 12bc  D530 sff(dc578av)
                1043 80a5  P5P800-MX Mainboard
        27b8  82801GB/GR (ICH7 Family) LPC Interface Bridge
                1028 01e6  PowerEdge 860
                103c 2a8c  Compaq 500B Microtower
-               1043 8179  P5KPL-VM Motherboard
+               1043 8179  P5B-MX/WiFi-AP, P5KPL-VM Motherboard
                107b 5048  E4500
                1462 7418  Wind PC MS-7418
                1775 11cc  CC11/CL11
                1028 01df  PowerEdge SC440
                1028 01e6  PowerEdge 860
                103c 2a8c  Compaq 500B Microtower
-               1043 8179  P5KPL-VM Motherboard
+               1043 8179  P5B-MX/WiFi-AP, P5KPL-VM Motherboard
                107b 5048  E4500
                1462 2310  MSI Hetis 945
                1462 7236  945P Neo3-F Rev. 2.2 motherboard
                103c 30a3  Compaq nw8440
                103c 30d5  530 Laptop
                1043 1237  A6J-Q008
-               1043 8179  P5KPL-VM,P5LD2-VM Mainboard
+               1043 8179  P5B-MX/WiFi-AP, P5KPL-VM, P5LD2-VM Mainboard
                1043 83ad  Eee PC 1015PX
                105b 0d7c  D270S/D250S Motherboard
                1071 8209  Medion MIM 2240 Notebook PC [MD98100]
                103c 30a1  NC2400
                103c 30a3  Compaq nw8440
                1043 1237  A6J-Q008
-               1043 8179  P5KPL-VM,P5LD2-VM Mainboard
+               1043 8179  P5B-MX/WiFi-AP, P5KPL-VM, P5LD2-VM Mainboard
                1043 83ad  Eee PC 1015PX
                105b 0d7c  D270S/D250S Motherboard
                1071 8209  Medion MIM 2240 Notebook PC [MD98100]
                103c 30a1  NC2400
                103c 30a3  Compaq nw8440
                1043 1237  A6J-Q008
-               1043 8179  P5KPL-VM,P5LD2-VM Mainboard
+               1043 8179  P5B-MX/WiFi-AP, P5KPL-VM, P5LD2-VM Mainboard
                1043 83ad  Eee PC 1015PX
                105b 0d7c  D270S/D250S Motherboard
                1071 8209  Medion MIM 2240 Notebook PC [MD98100]
                103c 30a1  NC2400
                103c 30a3  Compaq nw8440
                1043 1237  A6J-Q008
-               1043 8179  P5KPL-VM,P5LD2-VM Mainboard
+               1043 8179  P5B-MX/WiFi-AP, P5KPL-VM, P5LD2-VM Mainboard
                1043 83ad  Eee PC 1015PX
                105b 0d7c  D270S/D250S Motherboard
                1071 8209  Medion MIM 2240 Notebook PC [MD98100]
                103c 30a3  Compaq nw8440
                103c 30d5  530 Laptop
                1043 1237  A6J-Q008
-               1043 8179  P5KPL-VM,P5LD2-VM Mainboard
+               1043 8179  P5B-MX/WiFi-AP, P5KPL-VM, P5LD2-VM Mainboard
                1043 83ad  Eee PC 1015PX
                105b 0d7c  D270S/D250S Motherboard
                1071 8209  Medion MIM 2240 Notebook PC [MD98100]
                1043 1123  A6J-Q008
                1043 13c4  G2P
                1043 817f  P5LD2-VM Mainboard (Realtek ALC 882 codec)
+               1043 8249  P5B-MX/WiFi-AP
                1043 8290  P5KPL-VM Motherboard
                1043 82ea  P5KPL-CM Motherboard
                1043 8437  Eee PC 1015PX
                1028 01e6  PowerEdge 860
                103c 2a3b  Pavilion A1512X
                103c 2a8c  Compaq 500B Microtower
-               1043 8179  P5KPL-VM Motherboard
+               1043 8179  P5B-MX/WiFi-AP, P5KPL-VM Motherboard
                105b 0d7c  D270S/D250S Motherboard
                1071 8209  Medion MIM 2240 Notebook PC [MD98100]
                10f7 8338  Panasonic CF-Y5 laptop
                103c 30a3  Compaq nw8440
                103c 30d5  530 Laptop
                1043 1237  A6J-Q008
-               1043 8179  P5KPL-VM Motherboard
+               1043 8179  P5B-MX/WiFi-AP, P5KPL-VM Motherboard
                107b 5048  E4500
                10f7 8338  Panasonic CF-Y5 laptop
                1462 7418  Wind PC MS-7418
                103c 30c0  Compaq 6710b
                103c 30cc  Pavilion dv6700
                103c 30d9  Presario C700
+               1043 1017  X58LE
                104d 9005  Vaio VGN-FZ260E
                104d 902d  VAIO VGN-NR120E
                17aa 20a5  ThinkPad R61
                103c 30c1  Compaq 6910p
                103c 30cc  Pavilion dv6700
                103c 30d9  Presario C700
+               1043 1017  X58LE
                104d 9005  Vaio VGN-FZ260E
                104d 902d  VAIO VGN-NR120E
                17aa 20a7  ThinkPad T61/R61
                103c 30c1  Compaq 6910p
                103c 30cc  Pavilion dv6700
                103c 30d9  Presario C700
+               1043 1017  X58LE
                1043 81ec  P5B
                104d 9005  Vaio VGN-FZ260E
                104d 902d  VAIO VGN-NR120E
                103c 30c1  Compaq 6910p
                103c 30cc  Pavilion dv6700
                103c 30d9  Presario C700
+               1043 1017  X58LE
                1043 81ec  P5B
                104d 9005  Vaio VGN-FZ260E
                104d 902d  VAIO VGN-NR120E
                103c 30c1  Compaq 6910p
                103c 30cc  Pavilion dv6700
                103c 30d9  Presario C700
+               1043 1017  X58LE
                1043 81ec  P5B
                104d 9005  Vaio VGN-FZ260E
                104d 902d  VAIO VGN-NR120E
                103c 30c0  Compaq 6710b
                103c 30c1  Compaq 6910p
                103c 30cc  Pavilion dv6700
+               1043 1017  X58LE
                1043 81ec  P5B
                104d 9005  Vaio VGN-FZ260E
                104d 902d  VAIO VGN-NR120E
                103c 30c0  Compaq 6710b
                103c 30c1  Compaq 6910p
                103c 30cc  Pavilion dv6700
+               1043 1017  X58LE
                1043 81ec  P5B
                104d 9005  Vaio VGN-FZ260E
                104d 902d  VAIO VGN-NR120E
                103c 30c1  Compaq 6910p
                103c 30cc  Pavilion dv6700
                103c 30d9  Presario C700
+               1043 1017  X58LE
                1043 81ec  P5B
                104d 9005  Vaio VGN-FZ260E
                104d 902d  VAIO VGN-NR120E
                103c 30c0  Compaq 6710b
                103c 30c1  Compaq 6910p
                103c 30cc  Pavilion dv6700
+               1043 1017  X58LE
                1043 81ec  P5B
                104d 9005  Vaio VGN-FZ260E
                104d 902d  VAIO VGN-NR120E
                1028 01f3  Inspiron 1420
                1028 022f  Inspiron 1525
                103c 30d9  Presario C700
+               1043 1017  X58LE
                1043 81ec  P5B
                104d 9005  Vaio VGN-FZ260E
                104d 9008  Vaio VGN-SZ79SN_C
        283f  82801H (ICH8 Family) PCI Express Port 1
                1028 01da  OptiPlex 745
                103c 30c1  Compaq 6910p
+               1043 1017  X58LE
                104d 902d  VAIO VGN-NR120E
                17aa 20ad  ThinkPad T61/R61
                17c0 4083  Medion WIM 2210 Notebook PC [MD96850]
        2841  82801H (ICH8 Family) PCI Express Port 2
                103c 30c1  Compaq 6910p
+               1043 1017  X58LE
                104d 902d  VAIO VGN-NR120E
                17aa 20ad  ThinkPad T61/R61
                17c0 4083  Medion WIM 2210 Notebook PC [MD96850]
        2843  82801H (ICH8 Family) PCI Express Port 3
+               1043 1017  X58LE
                104d 902d  VAIO VGN-NR120E
                17aa 20ad  ThinkPad T61/R61
                17c0 4083  Medion WIM 2210 Notebook PC [MD96850]
        2845  82801H (ICH8 Family) PCI Express Port 4
+               1043 1017  X58LE
                17aa 20ad  ThinkPad T61/R61
                17c0 4083  Medion WIM 2210 Notebook PC [MD96850]
        2847  82801H (ICH8 Family) PCI Express Port 5
                103c 30c1  Compaq 6910p
                103c 30cc  Pavilion dv6700
                1043 1339  M51S series
+               1043 17f3  X58LE
                1043 81ec  P5B
                104d 9005  Vaio VGN-FZ260E
                104d 9008  Vaio VGN-SZ79SN_C
                103c 30c1  Compaq 6910p
                103c 30cc  Pavilion dv6700
                103c 30d9  Presario C700
+               1043 1017  X58LE
                104d 9005  Vaio VGN-FZ260E
                104d 902d  VAIO VGN-NR120E
                17aa 20a6  ThinkPad T61/R61
                17c0 4083  Medion WIM 2210 Notebook PC [MD96850]
                e4bf cc47  CCG-RUMBA
+       28c0  Volume Management Device NVMe RAID Controller
        2912  82801IH (ICH9DH) LPC Interface Controller
        2914  82801IO (ICH9DO) LPC Interface Controller
                1028 0211  Optiplex 755
        294c  82566DC-2 Gigabit Network Connection
                17aa 302e  82566DM-2 Gigabit Network Connection
        2970  82946GZ/PL/GL Memory Controller Hub
+               1043 823b  P5B-MX/WiFi-AP
        2971  82946GZ/PL/GL PCI Express Root Port
        2972  82946GZ/GL Integrated Graphics Controller
+               1043 823b  P5B-MX/WiFi-AP
        2973  82946GZ/GL Integrated Graphics Controller
        2974  82946GZ/GL HECI Controller
        2975  82946GZ/GL HECI Controller
                103c 30c1  Compaq 6910p
                103c 30cc  Pavilion dv6700
                103c 30d9  Presario C700
+               1043 1017  X58LE
                104d 9005  Vaio VGN-FZ260E
                104d 902d  VAIO VGN-NR120E
                17aa 20b1  ThinkPad T61
                1028 022f  Inspiron 1525
                103c 30c0  Compaq 6710b
                103c 30d9  Presario C700
+               1043 14e2  X58LE
                104d 902d  VAIO VGN-NR120E
                17aa 20b5  GM965 [X3100] on ThinkPad T61/R61
                17c0 4082  GM965 on Medion WIM 2210 Notebook PC [MD96850]
                1028 022f  Inspiron 1525
                103c 30c0  Compaq 6710b
                103c 30d9  Presario C700
+               1043 14e2  X58LE
                104d 902d  VAIO VGN-NR120E
                17aa 20b5  GM965 [X3100] on ThinkPad T61/R61
                17c0 4082  GM965 on Medion WIM 2210 Notebook PC [MD96850]
                1028 02da  OptiPlex 980
                1028 040a  Latitude E6410
                1028 040b  Latitude E6510
+               103c 1521  EliteBook 8540p
                144d c06a  R730 Laptop
                15d9 060d  C7SIM-Q Motherboard
                17c0 10d2  Medion Akoya E7214 Notebook PC [MD98410]
        3e1f  8th Gen Core 4-core Desktop Processor Host Bridge/DRAM Registers [Coffee Lake S]
                1458 5000  Z370 AORUS Gaming K3-CF
        3e30  8th Gen Core 8-core Desktop Processor Host Bridge/DRAM Registers [Coffee Lake S]
+       3e33  8th/9th Gen Core Processor Host Bridge/DRAM Registers [Coffee Lake]
        3e34  Coffee Lake HOST and DRAM Controller
        3e81  8th Gen Core Processor PCIe Controller (x16)
        3e85  8th Gen Core Processor PCIe Controller (x8)
                8086 1311  WiMAX/WiFi Link 5150 AGN
                8086 1316  WiMAX/WiFi Link 5150 ABG
        444e  Turbo Memory Controller
+       467f  Volume Management Device NVMe RAID Controller
+       4c3d  Volume Management Device NVMe RAID Controller
        5001  LE80578
        5002  LE80578 Graphics Processor Unit
        5009  LE80578 Video Display Controller
        5901  Xeon E3-1200 v6/7th Gen Core Processor PCIe Controller (x16)
        5902  HD Graphics 610
        5904  Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers
+               1025 115f  Aspire E5-575G
                17aa 2247  ThinkPad T570
                17aa 224f  ThinkPad X1 Carbon 5th Gen
        5905  Xeon E3-1200 v6/7th Gen Core Processor PCIe Controller (x8)
        5914  Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers
                17aa 225d  ThinkPad T480
        5916  HD Graphics 620
+               1025 1094  Aspire E5-575G
                17aa 2248  ThinkPad T570
                17aa 224f  ThinkPad X1 Carbon 5th Gen
        5917  UHD Graphics 620
        9622  Integrated RAID
        9641  Integrated RAID
        96a1  Integrated RAID
+       9a0b  Volume Management Device NVMe RAID Controller
        9b41  UHD Graphics
        9c00  8 Series SATA Controller 1 [IDE mode]
        9c01  8 Series SATA Controller 1 [IDE mode]
        9ce5  Wildcat Point-LP Serial IO GSPI Controller #0
        9ce6  Wildcat Point-LP Serial IO GSPI Controller #1
        9d03  Sunrise Point-LP SATA Controller [AHCI mode]
+               1025 115f  Acer Aspire E5-575G
                1028 06dc  Latitude E7470
                1028 06f3  Latitude 3570
                103c 8079  EliteBook 840 G3
        9d19  Sunrise Point-LP PCI Express Root Port #10
        9d1a  Sunrise Point-LP PCI Express Root Port #11
        9d21  Sunrise Point-LP PMC
+               1025 115f  Acer Aspire E5-575G
                1028 06dc  Latitude E7470
                1028 06f3  Latitude 3570
                103c 8079  EliteBook 840 G3
                17aa 225d  ThinkPad T480
                17aa 382a  B51-80 Laptop
        9d23  Sunrise Point-LP SMBus
+               1025 115f  Acer Aspire E5-575G
                1028 06dc  Latitude E7470
                1028 06f3  Latitude 3570
                103c 8079  EliteBook 840 G3
        9d2a  Sunrise Point-LP Serial IO SPI Controller #1
        9d2d  Sunrise Point-LP Secure Digital IO Controller
        9d2f  Sunrise Point-LP USB 3.0 xHCI Controller
+               1025 115f  Acer Aspire E5-575G
                1028 06dc  Latitude E7470
                1028 06f3  Latitude 3570
                103c 8079  EliteBook 840 G3
                17aa 225d  ThinkPad T480
                17aa 382a  B51-80 Laptop
        9d31  Sunrise Point-LP Thermal subsystem
+               1025 115f  Acer Aspire E5-575G
                1028 06dc  Latitude E7470
                1028 06f3  Latitude 3570
                103c 8079  EliteBook 840 G3
                17aa 382a  B51-80 Laptop
        9d35  Sunrise Point-LP Integrated Sensor Hub
        9d3a  Sunrise Point-LP CSME HECI #1
+               1025 115f  Acer Aspire E5-575G
                1028 06dc  Latitude E7470
                1028 06f3  Latitude 3570
                103c 8079  EliteBook 840 G3
        9d50  Sunrise Point LPC Controller
        9d56  Sunrise Point-LP LPC Controller
        9d58  Sunrise Point-LP LPC Controller
+               1025 115f  Acer Aspire E5-575G
                17aa 2247  ThinkPad T570
                17aa 224f  ThinkPad X1 Carbon 5th Gen
        9d60  Sunrise Point-LP Serial IO I2C Controller #0
+               1025 115f  Acer Aspire E5-575G
                1028 06f3  Latitude 3570
                103c 8079  EliteBook 840 G3
                17aa 225d  ThinkPad T480
                103c 8079  EliteBook 840 G3
                17aa 382a  B51-80 Laptop
        9d71  Sunrise Point-LP HD Audio
+               1025 1094  Acer Aspire E5-575G
                17aa 224f  ThinkPad X1 Carbon 5th Gen
                17aa 225d  ThinkPad T480
        9d84  Cannon Point-LP LPC Controller
        9da4  Cannon Point-LP SPI Controller
        9db0  Cannon Point-LP PCI Express Root Port #9
        9db1  Cannon Point-LP PCI Express Root Port #10
+       9db2  Cannon Point-LP PCI Express Root Port #1
        9db4  Cannon Point-LP PCI Express Root Port #13
                1028 089e  Inspiron 5482
        9db6  Cannon Point-LP PCI Express Root Port #15
                1028 089e  Inspiron 5482
        9dd3  Cannon Point-LP SATA Controller [AHCI Mode]
        9de0  Cannon Point-LP MEI Controller #1
+       9de3  Cannon Point-LP Keyboard and Text (KT) Redirection
        9de8  Cannon Point-LP Serial IO I2C Controller #0
                1028 089e  Inspiron 5482
        9de9  Cannon Point-LP Serial IO I2C Controller #1
        a102  Q170/Q150/B150/H170/H110/Z170/CM236 Chipset SATA Controller [AHCI Mode]
        a103  HM170/QM170 Chipset SATA Controller [AHCI Mode]
                1028 06e4  XPS 15 9550
+               103c 825b  OMEN-17-w001nv
        a105  Sunrise Point-H SATA Controller [RAID mode]
        a106  Q170/H170/Z170/CM236 Chipset SATA Controller [RAID Mode]
        a107  HM170/QM170 Chipset SATA Controller [RAID Mode]
        a120  100 Series/C230 Series Chipset Family P2SB
        a121  100 Series/C230 Series Chipset Family Power Management Controller
                1028 06e4  XPS 15 9550
+               103c 825b  OMEN-17-w001nv
        a122  Sunrise Point-H cAVS
        a123  100 Series/C230 Series Chipset Family SMBus
                1028 06e4  XPS 15 9550
+               103c 825b  OMEN-17-w001nv
        a124  100 Series/C230 Series Chipset Family SPI Controller
        a125  100 Series/C230 Series Chipset Family Gigabit Ethernet Controller
        a126  100 Series/C230 Series Chipset Family Trace Hub
        a12a  100 Series/C230 Series Chipset Family Serial IO GSPI #1
        a12f  100 Series/C230 Series Chipset Family USB 3.0 xHCI Controller
                1028 06e4  XPS 15 9550
+               103c 825b  OMEN-17-w001nv
        a130  100 Series/C230 Series Chipset Family USB Device Controller (OTG)
        a131  100 Series/C230 Series Chipset Family Thermal Subsystem
                1028 06e4  XPS 15 9550
+               103c 825b  OMEN-17-w001nv
        a133  Sunrise Point-H Northpeak ACPI Function
        a135  100 Series/C230 Series Chipset Family Integrated Sensor Hub
        a13a  100 Series/C230 Series Chipset Family MEI Controller #1
                1028 06e4  XPS 15 9550
+               103c 825b  OMEN-17-w001nv
        a13b  100 Series/C230 Series Chipset Family MEI Controller #2
        a13c  100 Series/C230 Series Chipset Family IDE Redirection
        a13d  100 Series/C230 Series Chipset Family KT Redirection
        a14d  QM170 Chipset LPC/eSPI Controller
        a14e  HM170 Chipset LPC/eSPI Controller
                1028 06e4  XPS 15 9550
+               103c 825b  OMEN-17-w001nv
        a14f  Sunrise Point-H LPC Controller
        a150  CM236 Chipset LPC/eSPI Controller
        a151  Sunrise Point-H LPC Controller
        a15f  Sunrise Point-H LPC Controller
        a160  100 Series/C230 Series Chipset Family Serial IO I2C Controller #0
                1028 06e4  XPS 15 9550
+               103c 825b  OMEN-17-w001nv
        a161  100 Series/C230 Series Chipset Family Serial IO I2C Controller #1
                1028 06e4  XPS 15 9550
        a162  100 Series/C230 Series Chipset Family Serial IO I2C Controller #2
        a16a  100 Series/C230 Series Chipset Family PCI Express Root Port #20
        a170  100 Series/C230 Series Chipset Family HD Audio Controller
                1028 06e4  XPS 15 9550
+               103c 825b  OMEN-17-w001nv
        a171  CM238 HD Audio Controller
        a182  C620 Series Chipset Family SATA Controller [AHCI mode]
        a186  C620 Series Chipset Family SATA Controller [RAID mode]
        a30c  QM370 Chipset LPC/eSPI Controller
        a323  Cannon Lake PCH SMBus Controller
        a324  Cannon Lake PCH SPI Controller
+       a328  Cannon Lake PCH Serial IO UART Host Controller
        a32c  Cannon Lake PCH PCI Express Root Port #21
        a32d  Cannon Lake PCH PCI Express Root Port #22
        a32e  Cannon Lake PCH PCI Express Root Port #23
        a353  Cannon Lake Mobile PCH SATA AHCI Controller
        a360  Cannon Lake PCH HECI Controller
        a363  Cannon Lake PCH Active Management Technology - SOL
+       a364  Cannon Lake PCH HECI Controller #2
        a368  Cannon Lake PCH Serial IO I2C Controller #0
        a369  Cannon Lake PCH Serial IO I2C Controller #1
        a36a  Cannon Lake PCH Serial IO I2C Controller #2
        d158  Core Processor Miscellaneous Registers
        f1a5  SSD 600P Series
        f1a6  SSD Pro 7600p/760p/E 6100p Series
+               8086 390b  Intel Corporation SSD Pro 7600p/760p/E 6100p Series [NVM Express]
        f1a8  SSD 660P Series
 8088  Beijing Wangxun Technology Co., Ltd.
+       0101  WX1860A2 Gigabit Ethernet Controller
+               8088 0201  Dual-Port Ethernet Network Adaptor SF200T
+       0102  WX1860A2S Gigabit Ethernet Controller
+               8088 0210  Dual-Port Ethernet Network Adaptor SF200T-S
+       0103  WX1860A4 Gigabit Ethernet Controller
+               8088 0401  Qual-Port Ethernet Network Adaptor SF400T
+               8088 0440  Qual-Port Ethernet Network Adaptor SF400-OCP
+       0104  WX1860A4S Gigabit Ethernet Controller
+               8088 0410  Qual-Port Ethernet Network Adaptor SF400T-S
+       0105  WX1860AL2 Gigabit Ethernet Controller
+               8088 0202  Dual-Port Ethernet Network Adaptor SF200HT
+       0106  WX1860AL2S Gigabit Ethernet Controller
+               8088 0220  Dual-Port Ethernet Network Adaptor SF200HT-S
+       0107  WX1860AL4 Gigabit Ethernet Controller
+               8088 0402  Qual-Port Ethernet Network Adaptor SF400HT
+       0108  WX1860AL4S Gigabit Ethernet Controller
+               8088 0420  Qual-Port Ethernet Network Adaptor SF400HT-S
        1001  Ethernet Controller RP1000 for 10GbE SFP+
                8088 0000  Ethernet Network Adaptor RP1000 for 10GbE SFP+
        2001  Ethernet Controller RP2000 for 10GbE SFP+
@@ -31769,6 +32110,10 @@ d161  Digium, Inc.
        8010  Wildcard A4B 4-port analog card (PCI-Express)
        8013  Wildcard TE236/TE436 quad-span T1/E1/J1 card
        b410  Wildcard B410 quad-BRI card
+d209  Ultimarc
+       1500  PAC Drive
+       15a2  SpinTrak
+       1601  AimTrak
 d4d4  Dy4 Systems Inc
        0601  PCI Mezzanine Card
 d531  I+ME ACTIA GmbH
@@ -31944,14 +32289,38 @@ f1d0  AJA Video
        c0ff  Kona/Xena 2
        cafe  Kona SD
        cfee  Xena LS/SD-22-DA/SD-DA
+       dafe  Corvid 1
        daff  KONA LHi
+       db00  IoExpress
        db01  Corvid22
+       db02  Kona 3G
+       db03  Corvid 3G
+       db04  Kona 3G QUAD
+       db05  Kona LHe+
+       db06  IoXT
+       db07  Kona 3G P2P
+       db08  Kona 3G QUAD P2P
        db09  Corvid 24
+       db11  T-Tap
        dcaf  Kona HD
        dfee  Xena HD-DA
+       eb07  Io4K
+       eb0a  Io4K UFC
+       eb0b  Kona 4
+       eb0c  Kona 4 UFC
        eb0d  Corvid 88
        eb0e  Corvid 44
-       eb1d  Kona 5
+       eb16  Corvid HEVC
+               10cf 1049  Corvid HEVC M31
+       eb18  Corvid HB-R
+       eb1a  Kona IP 1SFP
+       eb1c  Kona IP 2SFP
+       eb1d  Io4KPlus
+       eb1e  IoIP
+       eb1f  Kona 5
+       eb23  Kona 1
+       eb24  Kona HDMI
+       eb25  Corvid 44 12g
        efac  Xena SD-MM/SD-22-MM
        facd  Xena HD-MM
 f5f5  F5 Networks, Inc.
index c8e299fd023128b73cd135fb083ffa3c38b8fb2d..1e63bb517751a20f0ec6b114987c80b29f196dfb 100644 (file)
@@ -9,8 +9,8 @@
 #      The latest version can be obtained from
 #              http://www.linux-usb.org/usb.ids
 #
-# Version: 2019.11.05
-# Date:    2019-11-05 20:34:06
+# Version: 2020.02.28
+# Date:    2020-02-28 20:34:06
 #
 
 # Vendors, devices and interfaces. Please keep sorted.
@@ -42,6 +42,7 @@
        a001  Digitus DA-71114 SATA
 0085  Boeye Technology Co., Ltd.
        0600  eBook Reader
+0102  miniSTREAK
 0105  Trust International B.V.
        145f  NW-3100 802.11b/g 54Mbps Wireless Network Adapter [zd1211]
 0127  IBP
        7617  AT76C505AS Wireless Adapter
        7800  Mini Album
        800c  Airspy HF+
+       ff01  WootingOne
        ff02  WootingTwo
        ff07  Tux Droid fish dongle
 03ec  Iwatsu America, Inc.
        c102  PhotoSmart 8000 series
        c111  Deskjet 1510
        c202  PhotoSmart 8200 series
+       c211  Deskjet 2540 series
        c302  DeskJet D2300
        c402  PhotoSmart D5100 series
        c502  PhotoSmart D6100 series
        8370  7 Port Hub
        8371  PS/2 Keyboard And Mouse
        8372  FT8U100AX Serial Port
+       8508  Selectronic SP PRO
        87d0  Cressi Dive Computer Interface
        8a28  Rainforest Automation ZigBee Controller
        8a98  TIAO Multi-Protocol Adapter
        9135  Rotary Pub alarm
        9136  Pulsecounter
        9e90  Marvell OpenRD Base/Client
+       9f08  CIB-1894 Conclusion SmartLink Box:
        9f80  Ewert Energy Systems CANdapter
        a6d0  Texas Instruments XDS100v2 JTAG / BeagleBone A3
        a951  HCP HIT GSM/GPRS modem [Cinterion MC55i]
        602a  i900
 040b  Weltrend Semiconductor
        0a68  Func MS-3 gaming mouse [WT6573F MCU]
+       2000  wired Keyboard [Dynex DX-WRK1401]
        2367  Human Interface Device [HP CalcPad 200 Calculator and Numeric Keypad]
        6510  Weltrend Bar Code Reader
        6520  Xploder Xbox Memory Unit (8MB)
        b700  Tacticalboard
 0450  DFI, Inc.
 0451  Texas Instruments, Inc.
+       0422  TUSB422 Port Controller with Power Delivery
        1234  Bluetooth Device
        1428  Hub
        1446  TUSB2040/2070 Hub
+       16a2  CC Debugger
        16a6  BM-USBD1 BlueRobin RF heart rate sensor receiver
+       16a8  CC2531 ZigBee
+       16ae  CC2531 Dongle
        2036  TUSB2036 Hub
        2046  TUSB2046 Hub
        2077  TUSB2077 Hub
        e012  TI-Nspire Calculator
        e013  Network Bridge
        e01c  Data Collection Sled [Nspire Lab Cradle, Nspire Datatracker Cradle]
+       e01e  Nspire\99 CX Navigator\99 Access Point
        e01f  Python Adapter (firmware install mode)
        e020  Python Adapter
        e022  Nspire CX II
        0002  Genius NetMouse Pro
        0003  Genius NetScroll+
        0006  Easy Mouse+
+       0007  Trackbar Emotion
        000b  NetMouse Wheel(P+U)
        000c  TACOMA Fingerprint V1.06.01
        000e  Genius NetScroll Optical
        00cb  Basic Optical Mouse v2.0
        00ce  Generic PPC Flash device
        00d1  Optical Mouse with Tilt Wheel
+       00d2  Notebook Optical Mouse with Tilt Wheel
        00da  eHome Infrared Receiver
        00db  Natural Ergonomic Keyboard 4000 V1.0
        00dd  Comfort Curve Keyboard 2000 V1.0
        07cd  Surface Keyboard
        07f8  Wired Keyboard 600 (model 1576)
        07fd  Nano Transceiver 1.1
+       0810  LifeCam HD-3000
        0900  Surface Dock Hub
        0901  Surface Dock Hub
        0902  Surface Dock Hub
        4d62  HP Laser Mobile Mini Mouse
        4d75  Rocketfish RF-FLBTAD Bluetooth Adapter
        4d81  Dell N889 Optical Mouse
+       4d8a  HP Multimedia Keyboard
        4d91  Laser mouse M-D16DL
        4d92  Optical mouse M-D17DR
        4db1  Dell Laptop Integrated Webcam 2Mpix
        4de3  HP 5-Button Optical Comfort Mouse
        4de7  webcam
        4e04  Lenovo Keyboard KB1021
+       4e6f  Acer Wired Keyboard Model KBAY211
 0463  MGE UPS Systems
        0001  UPS
        ffff  UPS
        00a1  SmartCard Reader Keyboard KC 1000 SC
        0106  R-300 Wireless Mouse Receiver
        010d  MX-Board 3.0 Keyboard
+       0180  Strait 3.0
        b090  Keyboard
        b091  Mouse
 046b  American Megatrends, Inc.
        0a45  960 Headset
        0a4d  G430 Surround Sound Gaming Headset
        0a5b  G933 Wireless Headset Dongle
+       0a5d  G933 Headset Battery Charger
        0a66  [G533 Wireless Headset Dongle]
        0b02  C-UV35 [Bluetooth Mini-Receiver] (HID proxy mode)
        8801  Video Camera
        c231  G13 Virtual Mouse
        c245  G400 Optical Mouse
        c246  Gaming Mouse G300
+       c247  G100S Optical Gaming Mouse
        c248  G105 Gaming Keyboard
        c24a  G600 Gaming Mouse
        c24c  G400s Optical Mouse
        c31f  Comfort Keyboard K290
        c326  Washable Keyboard K310
        c328  Corded Keyboard K280e
+       c32b  G910 Orion Spark Mechanical Keyboard
        c332  G502 Proteus Spectrum Optical Mouse
        c335  G910 Orion Spectrum Mechanical Keyboard
        c33a  G413 Gaming Keyboard
        c531  C-U0007 [Unifying Receiver]
        c532  Unifying Receiver
        c534  Unifying Receiver
+       c537  Cordless Mouse Receiver
+       c53a  PowerPlay Wireless Charging System
        c603  3Dconnexion Spacemouse Plus XT
        c605  3Dconnexion CADman
        c606  3Dconnexion Spacemouse Classic
        f101  Atlas Modem
 047f  Plantronics, Inc.
        0101  Bulk Driver
+       02ee  BT600
        0301  Bulk Driver
        0411  Savi Office Base Station
        0ca1  USB DSP v4 Audio Interface
        af01  DA80
        c008  Audio 655 DSP
        c00e  Blackwire C310 headset
+       c03b  HD1
 0480  Toshiba America Inc
        0001  InTouch Module
        0004  InTouch Module
        0200  External Disk
        0820  Canvio Advance Disk
        0821  Canvio Advance 2TB model DTC920
+       0900  MQ04UBF100
        a006  External Disk 1.5TB
        a007  External Disk USB 3.0
        a009  Stor.E Basics
        8259  Probe
        91d1  Sensor Hub
        a171  ThermaData WiFi
+       a2e0  BMeasure instrument
        df11  STM Device in DFU Mode
        ff10  Swann ST56 Modem
 0484  Specialix
 048d  Integrated Technology Express, Inc.
        1165  IT1165 Flash Controller
        1172  Flash Drive
+       1234  Mass storage
        1336  SD/MMC Cardreader
        1345  Multi Cardreader
        9006  IT9135 BDA Afatech DVB-T HDTV Dongle
        10c9  PIXMA iP4600 Printer
        10ca  PIXMA iP3600 Printer
        10e3  PIXMA iX6850 Printer
+       12fe  Printer in service mode
        1404  W6400PG
        1405  W8400PG
        150f  BIJ2350 PCL
        178a  PIXMA MG3600 Series
        178d  PIXMA MG6853
        180b  PIXMA MG3000 series
+       1856  PIXMA TS6250
        1900  CanoScan LiDE 90
        1901  CanoScan 8800F
        1904  CanoScan LiDE 100
        2633  LASERCLASS 500
        2634  PC-D300/FAX-L400/ICD300
        2635  MPC190
+       2636  LBP3200
        2637  iR C6800
        2638  iR C3100
        263c  PIXMA MP360
        264f  MF5650 (FAX)
        2650  iR 6800C EUR
        2651  iR 3100C EUR
+       2654  LBP3600
        2655  FP-L170/MF350/L380/L398
        2656  iR1510-1670 CAPT Printer
+       2657  LBP3210
        2659  MF8100
        265b  CAPT Printer
        265c  iR C3220
        2666  iR C5800
        2667  iR85PLUS
        2669  iR105PLUS
-       266a  CAPT Device
+       266a  LBP3000
        266b  iR8070
        266c  iR9070
        266d  iR 5800C EUR
        2676  LBP2900
        2677  iR C2570
        2678  iR 2570C EUR
-       2679  CAPT Device
+       2679  LBP5000
        267a  iR2016
        267b  iR2020
        267d  MF7100 series
+       267e  LBP3300
        2684  MF3200 series
        2686  MF6500 series
        2687  iR4530
        2688  LBP3460
        2689  FAX-L180/L380S/L398S
        268a  LC310/L390/L408S
+       268b  LBP3500
        268c  iR C6870
        268d  iR 6870C EUR
        268e  iR C5870
        268f  iR 5870C EUR
        2691  iR7105
+       26a1  LBP5300
        26a3  MF4100 series
+       26a4  LBP5100
        26b0  MF4600 series
        26b4  MF4010 series
        26b5  MF4200 series
        26b6  FAX-L140/L130
-       26da  LBP3010B printer
+       26b9  LBP3310
+       26ba  LBP5050
+       26da  LBP3010/LBP3018/LBP3050
+       26db  LBP3100/LBP3108/LBP3150
        26e6  iR1024
+       26ea  LBP9100C
+       26ee  MF4320-4350
+       26f1  LBP7200C
+       26ff  LBP6300
        271a  LBP6000
+       271b  LBP6200
+       271c  LBP7010C/7018C
        2736  I-SENSYS MF4550d
        2737  MF4410
+       2771  LBP6020
+       2796  LBP6230/6240
        3041  PowerShot S10
        3042  CanoScan FS4000US Film Scanner
        3043  PowerShot S20
        0429  D5100
        042a  D800 (ptp)
        0430  D7100
+       0436  D810
        043f  D5600
        0f03  PD-10 Wireless Printer Adapter
        4000  Coolscan LS 40 ED
        4611  Storage Adapter FX2 (CY)
        4616  Flash Disk (TPP)
        4624  DS-Xtreme Flash Card
+       4717  West Bridge
        5201  Combi Keyboard-Hub (Hub)
        5202  Combi Keyboard-Hub (Keyboard)
        5500  HID->COM RS232 Adapter
 09c1  Arris Interactive LLC
        1337  TOUCHSTONE DEVICE
 09c2  Nisca Corp.
-09c3  ActivCard, Inc.
+09c3  HID Global
        0007  Reader V2
        0008  ZFG-9800-AC SmartCard Reader
        0014  ActivIdentity ActivKey SIM USB Token
+       0028  Crescendo Key
+       0029  Crescendo Key
+       002a  Crescendo Key
+       002b  Crescendo Key
+       002c  Crescendo Key
+       002e  Crescendo Key
 09c4  ACTiSYS Corp.
        0011  ACT-IR2000U IrDA Dongle
 09c5  Memory Corp.
        6323  USB Electronic Scale
 2237  Kobo Inc.
        4161  eReader White
+224f  APDM
+       0001  Access Point
+       0002  Docking Station
+       0004  V2 Opal ACM
+       0005  V2 Opal
+       0006  V2 Docking Station
+       0007  V2 Access Point ACM
+       0008  V2 Access Point
 225d  Morpho
        0001  FINGER VP Multimodal Biometric Sensor
        0008  CBM-E3 Fingerprint Sensor
 2548  Pulse-Eight
        1001  CEC Adapter
        1002  CEC Adapter
+25b5  FlatFrog
+       0002  Multitouch 3200
 2632  TwinMOS
        3209  7-in-1 Card Reader
 2639  Xsens
index 28f14891d9bbdd67df324c513d8cdb61421e58e5..5a0a3c287102805b454395bf17f673978151b8aa 100644 (file)
@@ -220,7 +220,7 @@ emergency.service    |              |              |
     so no filesystems can be mounted before the check is complete.
 
     When the root device becomes available,
-    <filename>initd-root-device.target</filename> is reached.
+    <filename>initrd-root-device.target</filename> is reached.
     If the root device can be mounted at
     <filename>/sysroot</filename>, the
     <filename>sysroot.mount</filename> unit becomes active and
index 69c156aacba756702c9abaa12427af416053289b..17c2c505edf0cf0e61f38aa96f3348c579fe0cbf 100644 (file)
   <refsect1>
     <title>Description</title>
 
-    <para>The <filename>environment.d</filename> directories contain a list of "global" environment
-    variable assignments for the user environment.
+    <para>The <filename>environment.d</filename> directories contain a list 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 to the services it
-    starts.</para>
+    parses them and updates the environment exported by the systemd user instance. See below for an
+    discussion of which processes inherit those variables.</para>
 
     <para>It is recommended to use numerical prefixes for file names to simplify ordering.</para>
 
     </refsect2>
   </refsect1>
 
+  <refsect1>
+    <title>Applicability</title>
+
+    <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
+    <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
+    them. Hint: in general,
+    <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    units contain programs launched by systemd, and
+    <citerefentry><refentrytitle>systemd.scope</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    units contain programs launched by something else.</para>
+
+    <para>Specifically, for ssh logins, the
+    <citerefentry project='die-net'><refentrytitle>sshd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    service builds an environment that is a combination of variables forwarded from the remote system and
+    defined by <command>sshd</command>, see the discussion in
+    <citerefentry project='die-net'><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+    A graphical display session will have an analogous mechanism to define the environment. Note that some
+    managers query the systemd user instance for the exported environment and inject this configuration into
+    programs they start, using <command>systemctl show-environment</command> or the underlying D-Bus call.
+    </para>
+  </refsect1>
+
   <refsect1>
     <title>See Also</title>
     <para>
index 75604b834221d64ff92146c7fd22ecf606feb7b1..f811b1034848c7c9dcf1c1ee919a55d574561328 100644 (file)
@@ -41,9 +41,8 @@
   <refsect1>
     <title>Description</title>
 
-    <para><command>halt</command>, <command>poweroff</command>,
-    <command>reboot</command> may be used to halt, power-off or reboot
-    the machine.</para>
+    <para><command>halt</command>, <command>poweroff</command>, <command>reboot</command> may be used to
+    halt, power-off, or reboot the machine. All three commands take the same options.</para>
 
   </refsect1>
 
   <refsect1>
     <title>Notes</title>
 
-    <para>These commands are implemented in a way that preserves compatibility with
-    the original SysV commands.
-    <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-    verbs <command>halt</command>, <command>poweroff</command>,
-    <command>reboot</command> provide the same functionality with some additional
-    features.</para>
+    <para>These commands are implemented in a way that preserves basic compatibility with the original SysV
+    commands.  <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    verbs <command>halt</command>, <command>poweroff</command>, <command>reboot</command> provide the same
+    functionality with some additional features.</para>
+
+    <para>Note that on many SysV systems <command>halt</command> used to be synonymous to
+    <command>poweroff</command>, i.e. both commands would equally result in powering the machine off. systemd
+    is more accurate here, and <command>halt</command> results in halting the machine only (leaving power
+    on), and <command>poweroff</command> is required to actually power it off.</para>
   </refsect1>
 
   <refsect1>
diff --git a/man/homectl.xml b/man/homectl.xml
new file mode 100644 (file)
index 0000000..ae502c8
--- /dev/null
@@ -0,0 +1,833 @@
+<?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="homectl" conditional='ENABLE_HOMED'
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>homectl</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>homectl</refentrytitle>
+    <manvolnum>1</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>homectl</refname>
+    <refpurpose>Create, remove, change or inspect home directories</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>homectl</command>
+      <arg choice="opt" rep="repeat">OPTIONS</arg>
+      <arg choice="req">COMMAND</arg>
+      <arg choice="opt" rep="repeat">NAME</arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>homectl</command> may be used to create, remove, change or inspect a user's home
+    directory. It's primarily a command interfacing with
+    <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    which manages home directories of users.</para>
+
+    <para>Home directories managed by <filename>systemd-homed.service</filename> are self-contained, and thus
+    include the user's full metadata record in the home's data storage itself, making them easy to migrate
+    between machines. In particular, a home directory describes a matching user record, and every user record
+    managed by <filename>systemd-homed.service</filename> also implies existence and encapsulation of a home
+    directory. The user account and home directory become the same concept.</para>
+
+    <para>The following backing storage mechanisms are supported:</para>
+
+    <itemizedlist>
+      <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
+      possible, even for the system administrator. This storage mechanism provides the strongest data
+      security and is thus recommended.</para></listitem>
+
+      <listitem><para>Similar, but the LUKS2 encrypted file system is located on regular block device, such
+      as an USB storage stick. In this mode home directories and all data they include are nicely migratable
+      between machines, simply by plugging the USB stick into different systems at different
+      times.</para></listitem>
+
+      <listitem><para>An encrypted directory using <literal>fscrypt</literal> on file systems that support it
+      (at the moment this is primarily <literal>ext4</literal>), located in
+      <filename>/home/*.homedir</filename>. This mechanism also provides encryption, but substantially
+      weaker than LUKS2, as most file system metadata is unprotected. Moreover
+      it currently does not support changing user passwords once the home directory has been
+      created.</para></listitem>
+
+      <listitem><para>A <literal>btrfs</literal> subvolume for each user, also located in
+      <filename>/home/*.homedir</filename>. This provides no encryption, but good quota
+      support.</para></listitem>
+
+      <listitem><para>A regular directory for each user, also located in
+      <filename>/home/*.homedir</filename>. This provides no encryption, but is a suitable fallback
+      available on all machines, even where LUKS2, <literal>fscrypt</literal> or <literal>btrfs</literal>
+      support is not available.</para></listitem>
+
+      <listitem><para>An individual Windows file share (CIFS) for each user.</para></listitem>
+    </itemizedlist>
+
+    <para>Note that <filename>systemd-homed.service</filename> and <command>homectl</command> will not manage
+    "classic" UNIX user accounts as created with <citerefentry
+    project='man-pages'><refentrytitle>useradd</refentrytitle><manvolnum>8</manvolnum></citerefentry> or
+    similar tools. In particular, this functionality is not suitable for managing system users (i.e. users
+    with a UID below 1000) but is exclusive to regular ("human") users.</para>
+
+    <para>Note that users/home directories managed via <command>systemd-homed.service</command> do not show
+    up in <filename>/etc/passwd</filename> and similar files, they are synthesized via glibc NSS during
+    runtime. They are thus resolvable and may be enumerated via the <citerefentry
+    project='man-pages'><refentrytitle>getent</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    tool.</para>
+
+    <para>This tool interfaces directly with <filename>systemd-homed.service</filename>, and may execute
+    specific commands on the home directories it manages. Since every home directory managed that way also
+    defines a JSON user and group record these home directories may also be inspected and enumerated via
+    <citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
+
+    <para>Home directories managed by <filename>systemd-homed.service</filename> are usually in one of two
+    states, or in a transition state between them: when <literal>active</literal> they are unlocked and
+    mounted, and thus accessible to the system and its programs; when <literal>inactive</literal> they are
+    not mounted and thus not accessible. Activation happens automatically at login of the user and usually
+    can only complete after a password (or other authentication token) has been supplied. Deactivation
+    happens after the user fully logged out. A home directory remains active as long as the user is logged in
+    at least once, i.e. has at least one login session. When the user logs in a second time simultaneously
+    the home directory remains active. It is deactivated only after the last of the user's sessions
+    ends.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+
+    <para>The following general options are understood (further options that control the various properties
+    of user records managed by <filename>systemd-homed.service</filename> are documented further
+    down):</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <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
+        <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>
+
+      <varlistentry>
+        <term><option>--json=</option><replaceable>FORMAT</replaceable></term>
+        <term><option>-J</option></term>
+
+        <listitem><para>Controls whether to output the user record in JSON format, if the
+        <command>inspect</command> command (see below) is used. Takes one of <literal>pretty</literal>,
+        <literal>short</literal> or <literal>off</literal>. If <literal>pretty</literal> human-friendly
+        whitespace and newlines are inserted in the output to make the JSON data more readable. If
+        <literal>short</literal> all superfluous whitespace is suppressed. If <literal>off</literal> (the
+        default) the user information is not shown in JSON format but in a friendly human readable formatting
+        instead. The <option>-J</option> option picks <literal>pretty</literal> when run interactively and
+        <literal>short</literal> otherwise.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--export-format=</option><replaceable>FORMAT</replaceable></term>
+        <term><option>-E</option></term>
+        <term><option>-EE</option></term>
+
+        <listitem><para>When used with the <command>inspect</command> verb in JSON mode (see above) may be
+        used to suppress certain aspects of the JSON user record on output. Specifically, if
+        <literal>stripped</literal> format is used the binding and runtime fields of the record are
+        removed. If <literal>minimal</literal> format is used the cryptographic signature is removed too. If
+        <literal>full</literal> format is used the full JSON record is shown (this is the default). This
+        option is useful for copying an existing user record to a different system in order to create a
+        similar user there with the same settings. Specifically: <command>homectl inspect -EE | ssh
+        root@othersystem homectl create -i-</command> may be used as simple command line for replicating a
+        user on another host. <option>-E</option> is equivalent to <option>-j --export-format=stripped</option>,
+        <option>-EE</option> to <option>-j --export-format=minimal</option>. Note that when replicating user
+        accounts user records acquired in <literal>stripped</literal> mode will retain the original
+        cryptographic signatures and thus may only be modified when the private key to update them is available
+        on the destination machine. When replicating users in <literal>minimal</literal> mode, the signature
+        is removed during the replication and thus the record will be implicitly signed with the key of the destination
+        machine and may be updated there without any private key replication.</para></listitem>
+      </varlistentry>
+
+      <xi:include href="user-system-options.xml" xpointer="host" />
+      <xi:include href="user-system-options.xml" xpointer="machine" />
+
+      <xi:include href="standard-options.xml" xpointer="no-pager" />
+      <xi:include href="standard-options.xml" xpointer="no-legend" />
+      <xi:include href="standard-options.xml" xpointer="no-ask-password" />
+      <xi:include href="standard-options.xml" xpointer="help" />
+      <xi:include href="standard-options.xml" xpointer="version" />
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>User Record Properties</title>
+
+    <para>The following options control various properties of the user records/home directories that
+    <filename>systemd-homed.service</filename> manages. These switches may be used in conjunction with the
+    <command>create</command> and <command>update</command> commands for configuring various aspects of the
+    home directory and the user account:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><option>--real-name=</option><replaceable>NAME</replaceable></term>
+        <term><option>-c</option> <replaceable>NAME</replaceable></term>
+
+        <listitem><para>The real name for the user. This corresponds with the GECOS field on classic UNIX NSS
+        records.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--realm=</option><replaceable>REALM</replaceable></term>
+
+        <listitem><para>The realm for the user. The realm associates a user with a specific organization or
+        installation, and allows distuingishing users of the same name defined in different contexts. The
+        realm can be any string that also qualifies as valid DNS domain name, and it is recommended to use
+        the organization's or installation's domain name for this purpose, but this is not enforced nor
+        required. On each system only a single user of the same name may exist, and if a user with the same
+        name and realm is seen it is assumed to refer to the same user while a user with the same name but
+        different realm is considered a different user. Note that this means that two users sharing the same
+        name but with distinct realms are not allowed on the same system. Assigning a realm to a user is
+        optional.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--email-address=</option><replaceable>EMAIL</replaceable></term>
+
+        <listitem><para>Takes an electronic mail address to associate with the user. On log-in the
+        <varname>$EMAIL</varname> environment variable is initialized from this value.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--location=</option><replaceable>TEXT</replaceable></term>
+
+        <listitem><para>Takes location specification for this user. This is free-form text, which might or
+        might not be usable by geo-location applications. Example: <option>--location="Berlin,
+        Germany"</option> or <option>--location="Basement, Room 3a"</option></para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--icon-name=</option><replaceable>ICON</replaceable></term>
+
+        <listitem><para>Takes an icon name to associate with the user, following the scheme defined by the <ulink
+        url="https://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html">Icon Naming
+        Specification</ulink>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--home-dir=</option><replaceable>PATH</replaceable></term>
+        <term><option>-d</option><replaceable>PATH</replaceable></term>
+
+        <listitem><para>Takes a path to use as home directory for the user. Note that this is the directory
+        the user's home directory is mounted to while the user is logged in. This is not where the user's
+        data is actually stored, see <option>--image-path=</option> for that. If not specified defaults to
+        <filename>/home/$USER</filename>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--uid=</option><replaceable>UID</replaceable></term>
+
+        <listitem><para>Takes a preferred numeric UNIX UID to assign this user. If a user is to be created
+        with the specified UID and it is already taken by a different user on the local system then creation
+        of the home directory is refused. Note though, if after creating the home directory it is used on a
+        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>
+
+        <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
+        GID separately is not permitted.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--member-of=</option><replaceable>GROUP</replaceable></term>
+        <term><option>-G</option> <replaceable>GROUP</replaceable></term>
+
+        <listitem><para>Takes a comma-separated list of auxiliary UNIX groups this user shall belong
+        to. Example: <option>--member-of=wheel</option> to provide the user with administrator
+        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>
+      </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>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--shell=</option><replaceable>SHELL</replaceable></term>
+
+        <listitem><para>Takes a file system path. Specifies the shell binary to execute on terminal
+        logins. If not specified defaults to <filename>/bin/bash</filename>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--setenv=</option><replaceable>VARIABLE</replaceable>=<replaceable>VALUE</replaceable></term>
+
+        <listitem><para>Takes an environment variable assignment to set for all user processes. Note that a
+        number of other settings also result in environment variables to be set for the user, including
+        <option>--email=</option>, <option>--timezone=</option> and <option>--language=</option>. May be used
+        multiple times to set multiple environment variables.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--timezone=</option><replaceable>TIMEZONE</replaceable></term>
+
+        <listitem><para>Takes a timezone specification as string that sets the timezone for the specified
+        user. Expects a `tzdata` location string. When the user logs in the <varname>$TZ</varname>
+        environment variable is initialized from this setting. Example:
+        <option>--timezone=Europe/Amsterdam</option> will result in the environment variable
+        <literal>TZ=:Europe/Amsterdam</literal>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--language=</option><replaceable>LANG</replaceable></term>
+
+        <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>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--ssh-authorized-keys=</option><replaceable>KEYS</replaceable></term>
+        <listitem><para>Either takes a SSH authorized key line to associate with the user record or a
+        <literal>@</literal> character followed by a path to a file to read one or more such lines from. SSH
+        keys configured this way are made available to SSH to permit access to this home directory and user
+        record. This option may be used more than once to configure multiple SSH keys.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--pkcs11-token-uri=</option><replaceable>URI</replaceable></term>
+        <listitem><para>Takes an RFC 7512 PKCS#11 URI referencing a security token (e.g. YubiKey or PIV
+        smartcard) that shall be able to unlock the user account. The security token URI should reference a
+        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>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--locked=</option><replaceable>BOOLEAN</replaceable></term>
+
+        <listitem><para>Takes a boolean argument. Specifies whether this user account shall be locked. If
+        true logins into this account are prohibited, if false (the default) they are permitted (of course,
+        only if authorization otherwise succeeds).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--not-before=</option><replaceable>TIMESTAMP</replaceable></term>
+        <term><option>--not-after=</option><replaceable>TIMESTAMP</replaceable></term>
+
+        <listitem><para>These options take a timestamp string, in the format documented in
+        <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry> and
+        configures points in time before and after logins into this account are not
+        permitted.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--rate-limit-interval=</option><replaceable>SECS</replaceable></term>
+        <term><option>--rate-limit-burst=</option><replaceable>NUMBER</replaceable></term>
+
+        <listitem><para>Configures a rate limit on authentication attempts for this user. If the user
+        attempts to authenticate more often than the specified number, on a specific system, within the
+        specified time interval authentication is refused until the time interval passes. Defaults to 10
+        times per 1min.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--password-hint=</option><replaceable>TEXT</replaceable></term>
+
+        <listitem><para>Takes a password hint to store alongside the user record. This string is stored
+        accessible only to privileged users and the user itself and may not be queried by other users.
+        Example: <option>--password-hint="My first pet's name"</option></para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--enforce-password-policy=</option><replaceable>BOOL</replaceable></term>
+        <term><option>-P</option></term>
+
+        <listitem><para>Takes a boolean argument. Configures whether to enforce the system's password policy
+        for this user, regarding quality and strength of selected passwords. Defaults to
+        on. <option>-P</option> is short for
+        <option>---enforce-password-policy=no</option>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--password-change-now=</option><replaceable>BOOL</replaceable></term>
+
+        <listitem><para>Takes a boolean argument. If true the user is asked to change their password on next
+        login.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--password-change-min=</option><replaceable>TIME</replaceable></term>
+        <term><option>--password-change-max=</option><replaceable>TIME</replaceable></term>
+        <term><option>--password-change-warn=</option><replaceable>TIME</replaceable></term>
+        <term><option>--password-change-inactive=</option><replaceable>TIME</replaceable></term>
+
+        <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,
+        <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>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--disk-size=</option><replaceable>BYTES</replaceable></term>
+        <listitem><para>Either takes a size in bytes as argument (possibly using the usual K, M, G, …
+        suffixes for 1024 base values), or a percentage value and configures the disk space to assign to the
+        user. If a percentage value is specified (i.e. the argument suffixed with <literal>%</literal>) it is
+        taken relative to the available disk space of the backing file system. If the LUKS2 backend is used
+        this configures the size of the loopback file and file system contained therein. For the other
+        storage backends configures disk quota using the filesystem's native quota logic, if available. If
+        not specified, defaults to 85% of the available disk space for the LUKS2 backend and to no quota for
+        the others.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--access-mode=</option><replaceable>MODE</replaceable></term>
+
+        <listitem><para>Takes a UNIX file access mode written in octal. Configures the access mode of the
+        home directory itself. Note that this is only used when the directory is first created, and the user
+        may change this any time afterwards. Example:
+        <option>--access-mode=0700</option></para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--umask=</option><replaceable>MASK</replaceable></term>
+
+        <listitem><para>Takes the access mode mask (in octal syntax) to apply to newly created files and
+        directories of the user ("umask"). If set this controls the initial umask set for all login sessions of
+        the user, possibly overriding the system's defaults.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--nice=</option><replaceable>NICE</replaceable></term>
+
+        <listitem><para>Takes the numeric scheduling priority ("nice level") to apply to the processes of the user at login
+        time. Takes a numeric value in the range -20 (highest priority) to 19 (lowest priority).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--rlimit=</option><replaceable>LIMIT</replaceable>=<replaceable>VALUE</replaceable><optional>:<replaceable>VALUE</replaceable></optional></term>
+
+        <listitem><para>Allows configuration of resource limits for processes of this user, see <citerefentry
+        project='man-pages'><refentrytitle>getrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+        for details. Takes a resource limit name (e.g. <literal>LIMIT_NOFILE</literal>) followed by an equal
+        sign, followed by a numeric limit. Optionally, separated by colon a second numeric limit may be
+        specified. If two are specified this refers to the soft and hard limits, respectively. If only one
+        limit is specified the setting sets both limits in one.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--tasks-max=</option><replaceable>TASKS</replaceable></term>
+
+        <listitem><para>Takes a non-zero unsigned integer as argument. Configures the maximum numer of tasks
+        (i.e. processes and threads) the user may have at any given time. This limit applies to all tasks
+        forked off the user's sessions, even if they change user identity via <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
+        <filename>user-$UID.slice</filename>. See
+        <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        for further details.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--memory-high=</option><replaceable>BYTES</replaceable></term>
+        <term><option>--memory-max=</option><replaceable>BYTES</replaceable></term>
+
+        <listitem><para>Set a limit on the memory a user may take up on a system at any given time in bytes
+        (the usual K, M, G, … suffixes are supported, to the base of 1024). This includes all memory used by
+        the user itself and all processes they forked off that changed user credentials. This controls the
+        <varname>MemoryHigh=</varname> and <varname>MemoryMax=</varname> settings 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>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--cpu-weight=</option><replaceable>WEIGHT</replaceable></term>
+        <term><option>--io-weight=</option><replaceable>WEIGHT</replaceable></term>
+
+        <listitem><para>Set a 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
+        <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        for further details.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--storage=</option><replaceable>STORAGE</replaceable></term>
+
+        <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>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--image-path=</option><replaceable>PATH</replaceable></term>
+
+        <listitem><para>Takes a file system path. Configures where to place the user's home directory. When
+        LUKS2 storage is used refers to the path to the loopback file, otherwise to the path to the home
+        directory. When unspecified defaults to <filename>/home/$USER.home</filename> when LUKS storage is
+        used and <filename>/home/$USER.homedir</filename> for the other storage mechanisms. Not defined for
+        the <literal>cifs</literal> storage mechanism. To use LUKS2 storage on a regular block device (for
+        example a USB stick) pass the path to the block device here.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--fs-type=</option><replaceable>TYPE</replaceable></term>
+
+        <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>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--luks-discard=</option><replaceable>BOOL</replaceable></term>
+
+        <listitem><para>When LUKS2 storage is used configures whether to enable the
+        <literal>discard</literal> feature of the file system. If enabled the file system on top of the LUKS2
+        volume will report empty block information to LUKS2 and the loopback file below, ensuring that empty
+        space in the home directory is returned to the backing file system below the LUKS2 volume, resulting
+        in a "sparse" loopback file. This option mostly defaults to off, since this permits over-committing
+        home directories which results in I/O errors if the underlying file system runs full while the upper
+        file system wants to allocate a block. Such I/O errors are generally not handled well by file systems
+        nor applications. When LUKS2 storage is used on top of regular block devices (instead of on top a
+        loopback file) the discard logic defaults to on.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--luks-cipher=</option><replaceable>CIPHER</replaceable></term>
+        <term><option>--luks-cipher-mode=</option><replaceable>MODE</replaceable></term>
+        <term><option>--luks-volume-key-size=</option><replaceable>BITS</replaceable></term>
+        <term><option>--luks-pbkdf-type=</option><replaceable>TYPE</replaceable></term>
+        <term><option>--luks-pbkdf-hash-algorithm=</option><replaceable>ALGORITHM</replaceable></term>
+        <term><option>--luks-pbkdf-time-cost=</option><replaceable>SECONDS</replaceable></term>
+        <term><option>--luks-pbkdf-memory-cost=</option><replaceable>BYTES</replaceable></term>
+        <term><option>--luks-pbkdf-parallel-threads=</option><replaceable>THREADS</replaceable></term>
+
+        <listitem><para>Configures various cryptographic parameters for the LUKS2 storage mechanism. See
+        <citerefentry
+        project='man-pages'><refentrytitle>cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        for details on the specific attributes.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--nosuid=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--nodev=</option><replaceable>BOOL</replaceable></term>
+        <term><option>--noexec=</option><replaceable>BOOL</replaceable></term>
+
+        <listitem><para>Configures the <literal>nosuid</literal>, <literal>nodev</literal> and
+        <literal>noexec</literal> mount options for the home directories. By default <literal>nodev</literal>
+        and <literal>nosuid</literal> are on, while <literal>noexec</literal> is off. For details about these
+        mount options see <citerefentry
+        project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--cifs-domain=</option><replaceable>DOMAIN</replaceable></term>
+        <term><option>--cifs-user-name=</option><replaceable>USER</replaceable></term>
+        <term><option>--cifs-service=</option><replaceable>SERVICE</replaceable></term>
+
+        <listitem><para>Configures the Windows File Sharing (CIFS) domain and user to associate with the home
+        directory/user account, as well as the file share ("service") to mount as directory. The latter is used when
+        <literal>cifs</literal> storage is selected.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--stop-delay=</option><replaceable>SECS</replaceable></term>
+
+        <listitem><para>Configures the time the per-user service manager shall continue to run after the all
+        sessions of the user ended. The default is configured in
+        <citerefentry><refentrytitle>logind.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> (for
+        home directories of LUKS2 storage located on removable media this defaults to 0 though). A longer
+        time makes sure quick, repetitive logins are more efficient as the user's service manager doesn't
+        have to be started every time.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--kill-processes=</option><replaceable>BOOL</replaceable></term>
+
+        <listitem><para>Configures whether to kill all processes of the user on logout. The default is
+        configured in
+        <citerefentry><refentrytitle>logind.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--auto-login=</option><replaceable>BOOL</replaceable></term>
+
+        <listitem><para>Takes a boolean argument. Configures whether the graphical UI of the system should
+        automatically log this user in if possible. Defaults to off. If less or more than one user is marked
+        this way automatic login is disabled.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Commands</title>
+
+    <para>The following commands are understood:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><command>list</command></term>
+
+        <listitem><para>List all home directories (along with brief details) currently managed by
+        <filename>systemd-homed.service</filename>. This command is also executed if none is specified on the
+        command line. (Note that the list of users shown by this command does not include users managed by
+        other subsystems, such as system users or any traditional users listed in
+        <filename>/etc/passwd</filename>.)</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>activate</command> <replaceable>USER</replaceable> [<replaceable>USER…</replaceable>]</term>
+
+        <listitem><para>Activate one or more home directories. The home directories of each listed user will
+        be activated and made available under their mount points (typically in
+        <filename>/home/$USER</filename>). Note that any home activated this way stays active indefinitely,
+        until it is explicitly deactivated again (with <command>deactivate</command>, see below), or the user
+        logs in and out again and it thus is deactivated due to the automatic deactivation-on-logout
+        logic.</para>
+
+        <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>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>deactivate</command> <replaceable>USER</replaceable> [<replaceable>USER…</replaceable>]</term>
+
+        <listitem><para>Deactivate one or more home directories. This undoes the effect of
+        <command>activate</command>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>inspect</command> <replaceable>USER</replaceable> [<replaceable>USER…</replaceable>]</term>
+
+        <listitem><para>Show various details about the specified home directories. This shows various
+        information about the home directory and its user account, including runtime data such as current
+        state, disk use and similar. Combine with <option>--json=</option> to show the detailed JSON user
+        record instead, possibly combined with <option>--export-format=</option> to suppress certain aspects
+        of the output.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>authenticate</command> <replaceable>USER</replaceable> [<replaceable>USER…</replaceable>]</term>
+
+        <listitem><para>Validate authentication credentials of a home directory. This queries the caller for
+        a password (or similar) and checks that it correctly unlocks the home directory. This leaves the home
+        directory in the state it is in, i.e. it leaves the home directory in inactive state if it was
+        inactive before, and in active state if it was active before.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>create</command> <replaceable>USER</replaceable></term>
+        <term><command>create</command> <option>--identity=</option><replaceable>PATH</replaceable> <optional><replaceable>USER</replaceable></optional></term>
+
+        <listitem><para>Create a new home directory/user account of the specified name. Use the various
+        user record property options (as documented above) to control various aspects of the home directory
+        and its user accounts.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>remove</command> <replaceable>USER</replaceable></term>
+
+        <listitem><para>Remove a home directory/user account. This will remove both the home directory's user
+        record and the home directory itself, and thus delete all files and directories owned by the
+        user.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>update</command> <replaceable>USER</replaceable></term>
+        <term><command>update</command> <option>--identity=</option><replaceable>PATH</replaceable> <optional><replaceable>USER</replaceable></optional></term>
+
+        <listitem><para>Update a home directory/user account. Use the various user record property options
+        (as documented above) to make changes to the account, or alternatively provide a full, updated JSON
+        user record via the <option>--identity=</option> option.</para>
+
+        <para>Note that changes to user records not signed by a cryptographic private key available locally
+        are not permitted, unless <option>--identity=</option> is used with a user record that is already
+        correctly signed by a recognized private key.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>passwd</command> <replaceable>USER</replaceable></term>
+
+        <listitem><para>Change the password of the specified home direcory/user account.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>resize</command> <replaceable>USER</replaceable> <replaceable>BYTES</replaceable></term>
+
+        <listitem><para>Change the disk space assigned to the specified home directory. If the LUKS2 storage
+        mechanism is used this will automatically resize the loopback file and the file system contained
+        within. Note that if <literal>ext4</literal> is used inside of the LUKS2 volume, it is necessary to
+        deactivate the home directory before shrinking it (i.e the user has to log out). Growing can be done
+        while the home directory is active. If <literal>xfs</literal> is used inside of the LUKS2 volume the
+        home directory may not be shrunk whatsoever. On all three of <literal>ext4</literal>,
+        <literal>xfs</literal> and <literal>btrfs</literal> the home directory may be grown while the user is
+        logged in, and on the latter also shrunk while the user is logged in. If the
+        <literal>subvolume</literal>, <literal>directory</literal>, <literal>fscrypt</literal> storage
+        mechanisms are used, resizing will change file system quota.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>lock</command> <replaceable>USER</replaceable></term>
+
+        <listitem><para>Temporarily suspend access to the user's home directory and remove any associated
+        cryptographic keys from memory. Any attempts to access the user's home directory will stall until the
+        home directory is unlocked again (i.e. re-authenticated). This functionality is primarily intended to
+        be used during system suspend to make sure the user's data cannot be accessed until the user
+        re-authenticates on resume. This operation is only defined for home directories that use the LUKS2
+        storage mechanism.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>unlock</command> <replaceable>USER</replaceable></term>
+
+        <listitem><para>Resume access to the user's home directory again, undoing the effect of
+        <command>lock</command> above. This requires authentication of the user, as the cryptographic keys
+        required for access to the home directory need to be reacquired.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>lock-all</command></term>
+
+        <listitem><para>Execute the <command>lock</command> command on all suitable home directories at
+        once. This operation is generally executed on system suspend (i.e. by <command>systemctl
+        suspend</command> and related commands), to ensure all active user's cryptographic keys for accessing
+        their home directories are removed from memory.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>with</command> <replaceable>USER</replaceable> <replaceable>COMMAND…</replaceable></term>
+
+        <listitem><para>Activate the specified user's home directory, run the specified command (under the
+        caller's identity, not the specified user's) and deactivate the home directory afterwards again
+        (unless the user is logged in otherwise). This command is useful for running privileged backup
+        scripts and such, but requires authentication with the user's credentials in order to be able to
+        unlock the user's home directory.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Exit status</title>
+
+    <para>On success, 0 is returned, a non-zero failure code otherwise.</para>
+  </refsect1>
+
+  <xi:include href="less-variables.xml" />
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Create a user <literal>waldo</literal> in the administrator group <literal>wheel</literal>, and
+      assign 500 MiB disk space to them.</title>
+
+      <programlisting>homectl create waldo --real-name="Waldo McWaldo" -G wheel --disk-size=500M</programlisting>
+    </example>
+
+    <example>
+      <title>Create a user <literal>wally</literal> on a USB stick, and assign a maximum of 500 concurrent
+      tasks to them.</title>
+
+      <programlisting>homectl create wally --real-name="Wally McWally" --image-path=/dev/disk/by-id/usb-SanDisk_Ultra_Fit_476fff954b2b5c44-0:0 --tasks-max=500</programlisting>
+    </example>
+
+    <example>
+      <title>Change nice level of user <literal>odlaw</literal> to +5 and make sure the environment variable
+      <varname>$SOME</varname> is set to the string <literal>THING</literal> for them on login.</title>
+
+      <programlisting>homectl update odlaw --nice=5 --setenv=SOME=THING</programlisting>
+    </example>
+
+    <example>
+      <title>Set up authentication with a YubiKey security token:</title>
+
+      <programlisting># Clear the Yubikey from any old keys (careful!)
+ykman piv reset
+
+# Generate a new private/public key pair on the device, store the public key in 'pubkey.pem'.
+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
+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 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>
+    </example>
+  </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>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>
+    </para>
+  </refsect1>
+
+</refentry>
index f6703b06d65c9f7b7cafec725d9132ba0fab6221..2a685d1ee653494c6f26d4d6c0fabfc2f3aaeaf8 100644 (file)
         priorities.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--facility=</option></term>
+
+        <listitem><para>Filter output by syslog facility. Takes a comma-separated list of numbers or facility
+        names. The names are the usual syslog facilities as documented in
+        <citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+        <option>--facility=help</option> may be used to display a list of known facility names and exit.
+        </para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>-g</option></term>
         <term><option>--grep=</option></term>
         </para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--namespace=<replaceable>NAMESPACE</replaceable></option></term>
+
+        <listitem><para>Takes a journal namespace identifier string as argument. If not specified the data
+        collected by the default namespace is shown. If specified shows the log data of the specified
+        namespace instead. If the namespace is specified as <literal>*</literal> data from all namespaces is
+        shown, interleaved. If the namespace identifier is prefixed with <literal>+</literal> data from the
+        specified namespace and the default namespace is shown, interleaved, but no other. For details about
+        journal namespaces see
+        <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--header</option></term>
 
index d16cb185803a1d1611e2fb2fca84b4ef5b317db0..e24c420ab00316c10d3847ce83432acaef14a92b 100644 (file)
@@ -18,6 +18,7 @@
   <refnamediv>
     <refname>journald.conf</refname>
     <refname>journald.conf.d</refname>
+    <refname>journald@.conf</refname>
     <refpurpose>Journal service configuration files</refpurpose>
   </refnamediv>
 
@@ -26,6 +27,7 @@
     <para><filename>/etc/systemd/journald.conf.d/*.conf</filename></para>
     <para><filename>/run/systemd/journald.conf.d/*.conf</filename></para>
     <para><filename>/usr/lib/systemd/journald.conf.d/*.conf</filename></para>
+    <para><filename>/etc/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf</filename></para>
   </refsynopsisdiv>
 
   <refsect1>
     <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for a general description of the syntax.</para>
 
+    <para>The <command>systemd-journald</command> instance managing the default namespace is configured by
+    <filename>/etc/systemd/journald.conf</filename> and associated drop-ins. Instances managing other
+    namespaces read <filename>/etc/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf</filename> with
+    the namespace identifier filled in. This allows each namespace to carry a distinct configuration. See
+    <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    for details about journal namespaces.</para>
   </refsect1>
 
   <xi:include href="standard-conf.xml" xpointer="main-conf" />
       <varlistentry>
         <term><varname>Storage=</varname></term>
 
-        <listitem><para>Controls where to store journal data. One of
-        <literal>volatile</literal>,
-        <literal>persistent</literal>,
-        <literal>auto</literal> and
-        <literal>none</literal>. If
-        <literal>volatile</literal>, journal
-        log data will be stored only in memory, i.e. below the
-        <filename>/run/log/journal</filename> hierarchy (which is
-        created if needed). If <literal>persistent</literal>, data
-        will be stored preferably on disk, i.e. below the
-        <filename>/var/log/journal</filename> hierarchy (which is
-        created if needed), with a fallback to
-        <filename>/run/log/journal</filename> (which is created if
-        needed), during early boot and if the disk is not writable.
-        <literal>auto</literal> is similar to
-        <literal>persistent</literal> but the directory
-        <filename>/var/log/journal</filename> is not created if
-        needed, so that its existence controls where log data goes.
-        <literal>none</literal> turns off all storage, all log data
-        received will be dropped. Forwarding to other targets, such as
-        the console, the kernel log buffer, or a syslog socket will
-        still work however. Defaults to
-        <literal>auto</literal>.</para></listitem>
+        <listitem><para>Controls where to store journal data. One of <literal>volatile</literal>,
+        <literal>persistent</literal>, <literal>auto</literal> and <literal>none</literal>. If
+        <literal>volatile</literal>, journal log data will be stored only in memory, i.e. below the
+        <filename>/run/log/journal</filename> hierarchy (which is created if needed). If
+        <literal>persistent</literal>, data will be stored preferably on disk, i.e. below the
+        <filename>/var/log/journal</filename> hierarchy (which is created if needed), with a fallback to
+        <filename>/run/log/journal</filename> (which is created if needed), during early boot and if the disk
+        is not writable.  <literal>auto</literal> is similar to <literal>persistent</literal> but the
+        directory <filename>/var/log/journal</filename> is not created if needed, so that its existence
+        controls where log data goes.  <literal>none</literal> turns off all storage, all log data received
+        will be dropped. Forwarding to other targets, such as the console, the kernel log buffer, or a syslog
+        socket will still work however. Defaults to <literal>auto</literal> in the default journal namespace,
+        and <literal>persistent</literal> in all others.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <varname>TTYPath=</varname>, described below.</para>
 
         <para>When forwarding to the kernel log buffer (kmsg), make sure to select a suitably large size for
-        the log buffer, and ensure the kernel's rate-limiting applied to userspace processes is turned
-        off. Specifically, add <literal>log_buf_len=8M</literal> and <literal>printk.devkmsg=on</literal> (or
-        similar) to the kernel command line.</para></listitem>
+        the log buffer, for example by adding <literal>log_buf_len=8M</literal> to the kernel command line.
+        <command>systemd</command> will automatically disable kernel's rate-limiting applied to userspace
+        processes (equivalent to setting <literal>printk.devkmsg=on</literal>).</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>MaxLevelWall=</varname></term>
 
         <listitem><para>Controls the maximum log level of messages
-        that are stored on disk, forwarded to syslog, kmsg, the
+        that are stored in the journal, forwarded to syslog, kmsg, the
         console or wall (if that is enabled, see above). As argument,
         takes one of
         <literal>emerg</literal>,
         are stored/forwarded, messages above are dropped. Defaults to
         <literal>debug</literal> for <varname>MaxLevelStore=</varname>
         and <varname>MaxLevelSyslog=</varname>, to ensure that the all
-        messages are written to disk and forwarded to syslog. Defaults
-        to
+        messages are stored in the journal and forwarded to syslog.
+        Defaults to
         <literal>notice</literal> for <varname>MaxLevelKMsg=</varname>,
         <literal>info</literal> for <varname>MaxLevelConsole=</varname>,
         and <literal>emerg</literal> for
       <varlistentry>
         <term><varname>ReadKMsg=</varname></term>
 
-        <listitem><para>Takes a boolean value. If enabled (the
-        default), journal reads <filename>/dev/kmsg</filename>
-        messages generated by the kernel.</para></listitem>
+        <listitem><para>Takes a boolean value. If enabled <command>systemd-journal</command> processes
+        <filename>/dev/kmsg</filename> messages generated by the kernel. In the default journal namespace
+        this option is enabled by default, it is disabled in all others.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index e34778ac218c4ebb31320ed32478dfbe460851f0..4906f7a330bb7895d59da4035cf8688526bd4051 100644 (file)
 
         <para>The operational status is one of the following:
             <variablelist>
+              <varlistentry>
+                <term>missing</term>
+                <listitem>
+                    <para>the device is missing</para>
+                </listitem>
+              </varlistentry>
               <varlistentry>
                 <term>off</term>
                 <listitem>
@@ -261,6 +267,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..6c60f6cf6b25f07b5a40d58329d1a250a097b66c 100644 (file)
         <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 8fde11867c69c599bab474e0d1f107f1c16e02f6..e343c406f29931b0e3eac1caa3507b6ec5128627 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 dynamic users and groups.</refpurpose>
+    <refpurpose>Provide UNIX user and group name resolution for user/group lookup via Varlink</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
   <refsect1>
     <title>Description</title>
 
-    <para><command>nss-systemd</command> is a plug-in module for the GNU Name Service Switch (NSS) functionality of the
-    GNU C Library (<command>glibc</command>), providing UNIX user and group name resolution for dynamic users and
-    groups allocated through the <varname>DynamicUser=</varname> option in systemd unit files. See
-    <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details on
-    this option.</para>
+    <para><command>nss-systemd</command> is a plug-in module for the GNU Name Service Switch (NSS)
+    functionality of the GNU C Library (<command>glibc</command>), providing UNIX user and group name
+    resolution for services implementing the <ulink url="https://systemd.io/USER_GROUP_API">User/Group Record
+    Lookup API via Varlink</ulink>, such as the system and service manager
+    <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>
 
     <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
     <filename>/etc/group</filename>, or if these files are missing.</para>
 
+    <para>This module preferably utilizes
+    <citerefentry><refentrytitle>systemd-userdbd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    for resolving users and groups, but also works without the service running.</para>
+
     <para>To activate the NSS module, add <literal>systemd</literal> to the lines starting with
     <literal>passwd:</literal> and <literal>group:</literal> in <filename>/etc/nsswitch.conf</filename>.</para>
 
@@ -54,7 +62,7 @@
 
     <!-- synchronize with other nss-* man pages and factory/etc/nsswitch.conf -->
     <programlisting>passwd:         compat mymachines <command>systemd</command>
-group:          compat mymachines <command>systemd</command>
+group:          compat [SUCCESS=merge] mymachines [SUCCESS=merge] <command>systemd</command>
 shadow:         compat
 
 hosts:          files mymachines resolve [!UNAVAIL=return] dns myhostname
index 470c1a4c4269add5d6facbada62d1486bfb26510..694371c2c47aeb3cf4c0fdc138266cd92fe777b4 100644 (file)
     <citerefentry><refentrytitle>systemd-logind.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
     and hence the systemd control group hierarchy.</para>
 
+    <para>The module also applies various resource management and runtime parameters to the new session, as
+    configured in the <ulink url="https://systemd.io/USER_RECORD">JSON User Record</ulink> of the user, when
+    one is defined.</para>
+
     <para>On login, this module — in conjunction with <filename>systemd-logind.service</filename> — ensures the
     following:</para>
 
       <listitem><para>A new systemd scope unit is created for the session. If this is the first concurrent session of
       the user, an implicit per-user slice unit below <filename>user.slice</filename> is automatically created and the
       scope placed into it. An instance of the system service <filename>user@.service</filename>, which runs the
-      systemd user manager instance, is started.  </para></listitem>
+      systemd user manager instance, is started.</para></listitem>
+
+      <listitem><para>The <literal>$TZ</literal>, <literal>$EMAIL</literal> and <literal>$LANG</literal>
+      environment variables are configured for the user, based on the respective data from the user's JSON
+      record (if it is defined). Moreover, any environment variables explicitly configured in the user record
+      are imported, and the umask, nice level, and resource limits initialized.</para></listitem>
     </orderedlist>
 
     <para>On logout, this module ensures the following:</para>
         is not set if the current user is not the original user of the session.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>$TZ</varname></term>
+        <term><varname>$EMAIL</varname></term>
+        <term><varname>$LANG</varname></term>
+
+        <listitem><para>If a JSON user record is known for the user logging in these variables are
+        initialized from the respective data in the record.</para></listitem>
+      </varlistentry>
+
     </variablelist>
 
     <para>The following environment variables are read by the module and may be used by the PAM service to pass
@@ -286,14 +304,23 @@ pam_set_data(handle, "systemd.runtime_max_sec", (void *)"3600", cleanup);
   <refsect1>
     <title>Example</title>
 
+    <para>Here's an example PAM configuration fragment that allows users sessions to be managed by
+    <filename>systemd-logind.service</filename>:</para>
+
     <programlisting>#%PAM-1.0
-auth       required     pam_unix.so
-auth       required     pam_nologin.so
-account    required     pam_unix.so
-password   required     pam_unix.so
-session    required     pam_unix.so
-session    required     pam_loginuid.so
-session    required     pam_systemd.so</programlisting>
+auth     sufficient pam_unix.so
+auth     required   pam_deny.so
+
+account  required   pam_nologin.so
+account  sufficient pam_unix.so
+account  required   pam_permit.so
+
+password sufficient pam_unix.so sha512 shadow try_first_pass try_authtok
+password required   pam_deny.so
+
+-session optional   pam_loginuid.so
+-session optional   pam_systemd.so
+session  required   pam_unix.so</programlisting>
   </refsect1>
 
   <refsect1>
@@ -303,6 +330,7 @@ session    required     pam_systemd.so</programlisting>
       <citerefentry><refentrytitle>systemd-logind.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>logind.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>loginctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>pam_systemd_home</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry project='man-pages'><refentrytitle>pam.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry project='man-pages'><refentrytitle>pam.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry project='man-pages'><refentrytitle>pam</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
diff --git a/man/pam_systemd_home.xml b/man/pam_systemd_home.xml
new file mode 100644 (file)
index 0000000..6dc1a83
--- /dev/null
@@ -0,0 +1,131 @@
+<?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="pam_systemd_home" conditional='ENABLE_PAM_HOME'>
+
+  <refentryinfo>
+    <title>pam_systemd_home</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>pam_systemd_home</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>pam_systemd_home</refname>
+    <refpurpose>Automatically mount home directories managed by <filename>systemd-homed.service</filename> on
+    login, and unmount them on logout</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <para><filename>pam_systemd_home.so</filename></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>pam_systemd_home</command> ensures that home directories managed by
+    <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    are automatically activated (mounted) on user login, and are deactivated (unmounted) when the last
+    session of the user ends.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+
+    <para>The following options are understood:</para>
+
+    <variablelist class='pam-directives'>
+
+      <varlistentry>
+        <term><varname>suspend=</varname></term>
+
+        <listitem><para>Takes a boolean argument. If true, the home directory of the user will be suspended
+        automatically during system suspend; if false it will remain active. Automatic suspending of the home
+        directory improves security substantially as secret key material is automatically removed from memory
+        before the system is put to sleep and must be re-acquired (through user re-authentication) when
+        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>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>debug</varname><optional>=</optional></term>
+
+        <listitem><para>Takes an optional boolean argument. If yes or without the argument, the module will log
+        debugging information as it operates.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Module Types Provided</title>
+
+    <para>The module provides all four management operations: <option>auth</option>, <option>account</option>,
+    <option>session</option>, <option>password</option>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Environment</title>
+
+    <para>The following environment variables are initialized by the module and available to the processes of the
+    user's session:</para>
+
+    <variablelist class='environment-variables'>
+      <varlistentry>
+        <term><varname>$SYSTEMD_HOME=1</varname></term>
+
+        <listitem><para>Indicates that the user's home directory is managed by <filename>systemd-homed.service</filename>.</para></listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Example</title>
+
+    <para>Here's an example PAM configuration fragment that permits users managed by
+    <filename>systemd-homed.service</filename> to log in:</para>
+
+    <programlisting>#%PAM-1.0
+auth      sufficient pam_unix.so
+-auth     sufficient pam_systemd_home.so
+auth      required   pam_deny.so
+
+account   required   pam_nologin.so
+-account  sufficient pam_systemd_home.so
+account   sufficient pam_unix.so
+account   required   pam_permit.so
+
+-password sufficient pam_systemd_home.so
+password  sufficient pam_unix.so sha512 shadow try_first_pass try_authtok
+password  required   pam_deny.so
+
+-session  optional   pam_keyinit.so revoke
+-session  optional   pam_loginuid.so
+-session  optional   pam_systemd_home.so
+-session  optional   pam_systemd.so
+session   required   pam_unix.so</programlisting>
+  </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>homed.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>homectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>pam.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>pam.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>pam</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index 88cf36ae252d8a08cee3697f363a0e806e095b6c..f2d8da40c410ffaff35f3100273fa5b2f09ed61c 100644 (file)
         <para>By default, after the unit files are attached the service manager's configuration is reloaded, except
         when <option>--no-reload</option> is specified (see above). This ensures that the new units made available to
         the service manager are seen by it.</para>
+
+        <para>If <option>--now</option> and/or <option>--enable</option> are passed, the portable service(s) are
+        immediately started (blocking operation unless <option>--no-block</option> is passed) and/or enabled after
+        attaching the image.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><command>detach</command> <replaceable>IMAGE</replaceable></term>
+        <term><command>detach</command> <replaceable>IMAGE</replaceable> [<replaceable>PREFIX…</replaceable>]</term>
 
         <listitem><para>Detaches a portable service image from the host. This undoes the operations executed by the
         <command>attach</command> command above, and removes the unit file copies, drop-ins and image symlink
         component of it (i.e. the file or directory name itself, not the path to it) is used for finding matching unit
         files. This is a convencience feature to allow all arguments passed as <command>attach</command> also to
         <command>detach</command>.</para></listitem>
+
+        <para>If <option>--now</option> and/or <option>--enable</option> are passed, the portable service(s) are
+        immediately stopped (blocking operation) and/or disabled before detaching the image. Prefix(es) are also accepted,
+        to be used in case the unit names do not match the image name as described in the <command>attach</command>.</para>
       </varlistentry>
 
       <varlistentry>
         contents of the image.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--enable</option></term>
+
+        <listitem><para>Immediately enable/disable the portable service after attaching/detaching.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--now</option></term>
+
+        <listitem><para>Immediately start/stop the portable service after attaching/before detaching.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--no-block</option></term>
+
+        <listitem><para>Don't block waiting for attach --now to complete.</para></listitem>
+      </varlistentry>
+
       <xi:include href="user-system-options.xml" xpointer="host" />
       <xi:include href="user-system-options.xml" xpointer="machine" />
 
diff --git a/man/repart.d.xml b/man/repart.d.xml
new file mode 100644 (file)
index 0000000..4993608
--- /dev/null
@@ -0,0 +1,388 @@
+<?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'>
+
+  <refentryinfo>
+    <title>repart.d</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>repart.d</refentrytitle>
+    <manvolnum>5</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>repart.d</refname>
+    <refpurpose>Partition Definition Files for Automatic Boot-Time Repartitioning</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <para><literallayout><filename>/etc/repart.d/*.conf</filename>
+<filename>/run/repart.d/*.conf</filename>
+<filename>/usr/lib/repart.d/*.conf</filename>
+    </literallayout></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><filename>repart.d/*.conf</filename> files describe basic properties of partitions of block
+    devices of the local system. They may be used to declare types, names and sizes of partitions that shall
+    exist. The
+    <citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    service reads these files and attempts to add new partitions currently missing and enlarge existing
+    partitions according to these definitions. Operation is generally incremental, i.e. when applied, what
+    exists already is left intact, and partitions are never shrunk, moved or deleted.</para>
+
+    <para>These definition files are useful for implementing operating system images that are prepared and
+    delivered with minimally sized images (for example lacking any state or swap partitions), and which on
+    first boot automatically take possession of any remaining disk space following a few basic rules.</para>
+
+    <para>Currently, support for partition definition files is only implemented for GPT partitition
+    tables.</para>
+
+    <para>Partition files are generally matched against any partitions already existing on disk in a simple
+    algorithm: the partition files are sorted by their filename (ignoring the directory prefix), and then
+    compared in order against existing partitions matching the same partition type UUID. Specifically, the
+    first existing partition with a specific partition type UUID is assigned the first definition file with
+    the same partition type UUID, and the second existing partition with a specific type UUID the second
+    partition file with the same type UUID, and so on. Any left-over partition files that have no matching
+    existing partition are assumed to define new partition that shall be created. Such partitions are
+    appended to the end of the partition table, in the order defined by their names utilizing the first
+    partition slot greater than the highest slot number currently in use. Any existing partitions that have
+    no matching partition file are left as they are.</para>
+
+    <para>Note that these partition definition files do not describe the contents of the partitions, such as
+    the file system used. Separate mechanisms, such as
+    <citerefentry><refentrytitle>systemd-growfs</refentrytitle><manvolnum>8</manvolnum></citerefentry> and
+    <command>systemd-makefs</command> maybe be used to initialize or grow the file systems inside of these
+    partitions.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>[Partition] Section Options</title>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>Type=</varname></term>
+
+        <listitem><para>The GPT partition type UUID to match. This may be a GPT partition type UUID such as
+        <constant>4f68bce3-e8cd-4db1-96e7-fbcaf984b709</constant>, or one of the following special
+        identifiers:</para>
+
+        <table>
+          <title>GPT partition type identifiers</title>
+
+          <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+            <colspec colname="name" />
+            <colspec colname="explanation" />
+
+            <thead>
+              <row>
+                <entry>Identifier</entry>
+                <entry>Explanation</entry>
+              </row>
+            </thead>
+
+            <tbody>
+              <row>
+                <entry><constant>esp</constant></entry>
+                <entry>EFI System Partition</entry>
+              </row>
+
+              <row>
+                <entry><constant>xbootldr</constant></entry>
+                <entry>Extended Boot Loader Partition</entry>
+              </row>
+
+              <row>
+                <entry><constant>swap</constant></entry>
+                <entry>Swap partition</entry>
+              </row>
+
+              <row>
+                <entry><constant>home</constant></entry>
+                <entry>Home (<filename>/home/</filename>) partition</entry>
+              </row>
+
+              <row>
+                <entry><constant>srv</constant></entry>
+                <entry>Server data (<filename>/srv/</filename>) partition</entry>
+              </row>
+
+              <row>
+                <entry><constant>var</constant></entry>
+                <entry>Variable data (<filename>/var/</filename>) partition</entry>
+              </row>
+
+              <row>
+                <entry><constant>tmp</constant></entry>
+                <entry>Temporary data (<filename>/var/tmp/</filename>) partition</entry>
+              </row>
+
+              <row>
+                <entry><constant>linux-generic</constant></entry>
+                <entry>Generic Linux file system partition</entry>
+              </row>
+
+              <row>
+                <entry><constant>root</constant></entry>
+                <entry>Root file system partition type appropriate for the local architecture (an alias for an architecture root file system partition type listed below, e.g. <constant>root-x86-64</constant>)</entry>
+              </row>
+
+              <row>
+                <entry><constant>root-verity</constant></entry>
+                <entry>Verity data for the root file system partition for the local architecture</entry>
+              </row>
+
+              <row>
+                <entry><constant>root-secondary</constant></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>
+                <entry><constant>root-secondary-verity</constant></entry>
+                <entry>Verity data for the root file system partition of the secondary architecture</entry>
+              </row>
+
+              <row>
+                <entry><constant>root-x86</constant></entry>
+                <entry>Root file system partition for the x86 (32bit, aka i386) architecture</entry>
+              </row>
+
+              <row>
+                <entry><constant>root-x86-verity</constant></entry>
+                <entry>Verity data for the x86 (32bit) root file system partition</entry>
+              </row>
+
+              <row>
+                <entry><constant>root-x86-64</constant></entry>
+                <entry>Root file system partition for the x86_64 (64bit, aka amd64) architecture</entry>
+              </row>
+
+              <row>
+                <entry><constant>root-x86-64-verity</constant></entry>
+                <entry>Verity data for the x86_64 (64bit) root file system partition</entry>
+              </row>
+
+              <row>
+                <entry><constant>root-arm</constant></entry>
+                <entry>Root file system partition for the ARM (32bit) architecture</entry>
+              </row>
+
+              <row>
+                <entry><constant>root-arm-verity</constant></entry>
+                <entry>Verity data for the ARM (32bit) root file system partition</entry>
+              </row>
+
+              <row>
+                <entry><constant>root-arm64</constant></entry>
+                <entry>Root file system partition for the ARM (64bit, aka aarch64) architecture</entry>
+              </row>
+
+              <row>
+                <entry><constant>root-arm64-verity</constant></entry>
+                <entry>Verity data for the ARM (64bit, aka aarch64) root file system partition</entry>
+              </row>
+
+              <row>
+                <entry><constant>root-ia64</constant></entry>
+                <entry>Root file system partition for the ia64 architecture</entry>
+              </row>
+
+              <row>
+                <entry><constant>root-ia64-verity</constant></entry>
+                <entry>Verity data for the ia64 root file system partition</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </table>
+
+        <para>This setting defaults to <constant>linux-generic</constant>.</para>
+
+        <para>Most of the partition type UUIDs listed above are defined in the <ulink
+        url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
+        Specification</ulink>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Label=</varname></term>
+
+        <listitem><para>The textual label 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 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>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Priority=</varname></term>
+
+        <listitem><para>A numeric priority to assign to this partition, in the range -2147483648…2147483647,
+        with smaller values indicating higher priority, and higher values indicating smaller priority. This
+        priority is used in case the configured size constraints on the defined partitions do not permit
+        fitting all partitions onto the available disk space. If the partitions do not fit, the highest
+        numeric partition priority of all defined partitions is determined, and all defined partitions with
+        this priority are removed from the list of new partitions to create (which may be multiple, if the
+        same priority is used for multiple partitions). The fitting algorithm is then tried again. If the
+        partitions still do not fit, the now highest numeric partition priority is determined, and the
+        matching partitions removed too, and so on. Partitions of a priority of 0 or lower are never
+        removed. If all partitions with a priority above 0 are removed and the partitions still do not fit on
+        the device the operation fails. Note that this priority has no effect on ordering partitions, for
+        that use the alphabetical order of the filenames of the partition definition files. Defaults to
+        0.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Weight=</varname></term>
+
+        <listitem><para>A numeric weight to assign to this partition in the range 0…1000000. Available disk
+        space is assigned the defined partitions according to their relative weights (subject to the size
+        constraints configured with <varname>SizeMinBytes=</varname>, <varname>SizeMaxBytes=</varname>), so
+        that a partition with weight 2000 gets double the space as one with weight 1000, and a partition with
+        weight 333 a third of that. Defaults to 1000.</para>
+
+        <para>The <varname>Weight=</varname> setting is used to distribute available disk space in an
+        "elastic" fashion, based on the disk size and existing partitions. If a partition shall have a fixed
+        size use both <varname>SizeMinBytes=</varname> and <varname>SizeMaxBytes=</varname> with the same
+        value in order to fixate the size to one value, in which case the weight has no
+        effect.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>PaddingWeight=</varname></term>
+
+        <listitem><para>Similar to <varname>Weight=</varname> but sets a weight for the free space after the
+        partition (the "padding"). When distributing available space the weights of all partitions and all
+        defined padding is summed, and then each partition and padding gets the fraction defined by its
+        weight. Defaults to 0, i.e. by default no padding is applied.</para>
+
+        <para>Padding is useful if empty space shall be left for later additions or a safety margin at the
+        end of the device or between partitions.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>SizeMinBytes=</varname></term>
+        <term><varname>SizeMaxBytes=</varname></term>
+
+        <listitem><para>Specifies minimum and maximum size constraints in bytes. Takes the usual K, M, G, T,
+        …  suffixes (to the base of 1024). If <varname>SizeMinBytes=</varname> is specified the partition is
+        created at or grown to at least the specified size. If <varname>SizeMaxBytes=</varname> is specified
+        the partition is created at or grown to at most the specified size. The precise size is determined
+        through the weight value value configured with <varname>Weight=</varname>, see above. When
+        <varname>SizeMinBytes=</varname> is set equal to <varname>SizeMaxBytes=</varname> the configured
+        weight has no effect as the partition is explicitly sized to the specified fixed value. Note that
+        partitions are never created smaller than 4096 bytes, and since partitions are never shrunk the
+        previous size of the partition (in case the partition already exists) is also enforced as lower bound
+        for the new size. The values should be specified as multiples of 4096 bytes, and are rounded upwards
+        (in case of <varname>SizeMinBytes=</varname>) or downwards (in case of
+        <varname>SizeMaxBytes=</varname>) otherwise. If the backing device does not provide enough space to
+        fulfill the constraints placing the partition will fail. For partitions that shall be created,
+        depending on the setting of <varname>Priority=</varname> (see above) the partition might be dropped
+        and the placing algorithm restarted. By default no size constraints are set.</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
+        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>FactoryReset=</varname></term>
+
+        <listitem><para>Takes a boolean argument. If specified the partition is marked for removal during a
+        factory reset operation. This functionality is useful to implement schemes where images can be reset
+        into their original state by removing partitions and creating them anew. Defaults to off.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Grow the root partition to the full disk size at first boot</title>
+
+      <para>With the following file the root partition is automatically grown to the full disk if possible during boot.</para>
+
+      <para><programlisting># /usr/lib/repart.d/50-root.conf
+[Partition]
+Type=root
+</programlisting></para>
+    </example>
+
+    <example>
+      <title>Create a swap and home partition automatically on boot, if missing</title>
+
+      <para>The home partition gets all available disk space while the swap partition gets 1G at most and 64M
+      at least. We set a priority > 0 on the swap partition to ensure the swap partition is not used if not
+      enough space is available. For every three bytes assigned to the home partition the swap partition gets
+      assigned one.</para>
+
+      <para><programlisting># /usr/lib/repart.d/60-home.conf
+[Partition]
+Type=home
+</programlisting></para>
+
+      <para><programlisting># /usr/lib/repart.d/70-swap.conf
+[Partition]
+Type=swap
+SizeMinBytes=64M
+SizeMaxBytes=1G
+Priority=1
+Weight=333
+</programlisting></para>
+    </example>
+
+    <example>
+      <title>Create B partitions in an A/B Verity setup, if missing</title>
+
+      <para>Let's say the vendor intends to update OS images in an A/B setup, i.e. with two root partitions
+      (and two matching Verity partitions) that shall be used alternatingly during upgrades. To minimize
+      image sizes the original image is shipped only with one root and one Verity partition (the "A" set),
+      and the second root and Verity partitions (the "B" set) shall be created on first boot on the free
+      space on the medium.</para>
+
+      <para><programlisting># /usr/lib/repart.d/50-root.conf
+[Partition]
+Type=root
+SizeMinBytes=512M
+SizeMaxBytes=512M
+</programlisting></para>
+
+      <para><programlisting># /usr/lib/repart.d/60-root-verity.conf
+[Partition]
+Type=root-verity
+SizeMinBytes=64M
+SizeMaxBytes=64M
+</programlisting></para>
+
+      <para>The definitions above cover the "A" set of root partition (of a fixed 512M size) and Verity
+      partition for the root partition (of a fixed 64M size). Let's use symlinks to create the "B" set of
+      partitions, since after all they shall have the same properties and sizes as the "A" set.</para>
+
+<para><programlisting># ln -s 50-root.conf /usr/lib/repart.d/70-root-b.conf
+# ln -s 60-root-verity.conf /usr/lib/repart.d/80-root-verity-b.conf
+</programlisting></para>
+    </example>
+
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>sfdisk</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index 0f70ced5b54f6aba824fee8d4f2cf54e7e95cff2..37161ebcbcc1ddbb976348fc1ec39d106d2a0a8d 100644 (file)
       <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
index f33621a654998e7fac8c9a9cf832a7805055cbd2..296dd7da3a73890cc913d7a395abc4112cc880ae 100644 (file)
@@ -17,13 +17,14 @@ manpages = [
  ['environment.d', '5', [], 'ENABLE_ENVIRONMENT_D'],
  ['file-hierarchy', '7', [], ''],
  ['halt', '8', ['poweroff', 'reboot'], ''],
+ ['homectl', '1', [], 'ENABLE_HOMED'],
  ['hostname', '5', [], ''],
  ['hostnamectl', '1', [], 'ENABLE_HOSTNAMED'],
  ['hwdb', '7', [], 'ENABLE_HWDB'],
  ['journal-remote.conf', '5', ['journal-remote.conf.d'], 'HAVE_MICROHTTPD'],
  ['journal-upload.conf', '5', ['journal-upload.conf.d'], 'HAVE_MICROHTTPD'],
  ['journalctl', '1', [], ''],
- ['journald.conf', '5', ['journald.conf.d'], ''],
+ ['journald.conf', '5', ['journald.conf.d', 'journald@.conf'], ''],
  ['kernel-command-line', '7', [], ''],
  ['kernel-install', '8', [], ''],
  ['libudev', '3', [], ''],
@@ -45,8 +46,10 @@ manpages = [
  ['nss-systemd', '8', ['libnss_systemd.so.2'], 'ENABLE_NSS_SYSTEMD'],
  ['os-release', '5', [], ''],
  ['pam_systemd', '8', [], 'HAVE_PAM'],
+ ['pam_systemd_home', '8', [], 'ENABLE_PAM_HOME'],
  ['portablectl', '1', [], 'ENABLE_PORTABLED'],
  ['pstore.conf', '5', ['pstore.conf.d'], 'ENABLE_PSTORE'],
+ ['repart.d', '5', [], 'ENABLE_REPART'],
  ['resolvectl', '1', ['resolvconf'], 'ENABLE_RESOLVE'],
  ['resolved.conf', '5', ['resolved.conf.d'], 'ENABLE_RESOLVE'],
  ['runlevel', '8', [], ''],
@@ -192,6 +195,7 @@ manpages = [
    'sd_bus_open_user_with_description',
    'sd_bus_open_with_description'],
   ''],
+ ['sd_bus_enqueue_for_read', '3', [], ''],
  ['sd_bus_error',
   '3',
   ['SD_BUS_ERROR_MAKE_CONST',
@@ -230,6 +234,7 @@ manpages = [
   ''],
  ['sd_bus_message_append_strv', '3', [], ''],
  ['sd_bus_message_copy', '3', [], ''],
+ ['sd_bus_message_dump', '3', [], ''],
  ['sd_bus_message_get_cookie', '3', ['sd_bus_message_get_reply_cookie'], ''],
  ['sd_bus_message_get_monotonic_usec',
   '3',
@@ -547,7 +552,9 @@ manpages = [
   ''],
  ['sd_journal_open',
   '3',
-  ['SD_JOURNAL_CURRENT_USER',
+  ['SD_JOURNAL_ALL_NAMESPACES',
+   'SD_JOURNAL_CURRENT_USER',
+   'SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE',
    'SD_JOURNAL_LOCAL_ONLY',
    'SD_JOURNAL_OS_ROOT',
    'SD_JOURNAL_RUNTIME_ONLY',
@@ -663,8 +670,11 @@ manpages = [
  ['systemd-backlight@.service', '8', ['systemd-backlight'], 'ENABLE_BACKLIGHT'],
  ['systemd-binfmt.service', '8', ['systemd-binfmt'], 'ENABLE_BINFMT'],
  ['systemd-bless-boot-generator', '8', [], 'ENABLE_EFI'],
- ['systemd-bless-boot.service', '8', [], 'ENABLE_EFI'],
- ['systemd-boot-check-no-failures.service', '8', [], ''],
+ ['systemd-bless-boot.service', '8', ['systemd-bless-boot'], 'ENABLE_EFI'],
+ ['systemd-boot-check-no-failures.service',
+  '8',
+  ['systemd-boot-check-no-failures'],
+  ''],
  ['systemd-boot-system-token.service', '8', [], 'ENABLE_EFI'],
  ['systemd-boot', '7', ['sd-boot'], 'ENABLE_EFI'],
  ['systemd-cat', '1', [], ''],
@@ -707,6 +717,7 @@ manpages = [
   '8',
   ['systemd-hibernate-resume'],
   'ENABLE_HIBERNATE'],
+ ['systemd-homed.service', '8', ['systemd-homed'], 'ENABLE_HOMED'],
  ['systemd-hostnamed.service', '8', ['systemd-hostnamed'], 'ENABLE_HOSTNAMED'],
  ['systemd-hwdb', '8', [], 'ENABLE_HWDB'],
  ['systemd-id128', '1', [], ''],
@@ -733,7 +744,10 @@ manpages = [
   ['systemd-journald',
    'systemd-journald-audit.socket',
    'systemd-journald-dev-log.socket',
-   'systemd-journald.socket'],
+   'systemd-journald-varlink@.socket',
+   'systemd-journald.socket',
+   'systemd-journald@.service',
+   'systemd-journald@.socket'],
   ''],
  ['systemd-localed.service', '8', ['systemd-localed'], 'ENABLE_LOCALED'],
  ['systemd-logind.service', '8', ['systemd-logind'], 'ENABLE_LOGIND'],
@@ -749,6 +763,10 @@ manpages = [
   ''],
  ['systemd-modules-load.service', '8', ['systemd-modules-load'], 'HAVE_KMOD'],
  ['systemd-mount', '1', ['systemd-umount'], ''],
+ ['systemd-network-generator.service',
+  '8',
+  ['systemd-network-generator'],
+  'ENABLE_NETWORKD'],
  ['systemd-networkd-wait-online.service',
   '8',
   ['systemd-networkd-wait-online'],
@@ -758,7 +776,7 @@ manpages = [
  ['systemd-nspawn', '1', [], ''],
  ['systemd-path', '1', [], ''],
  ['systemd-portabled.service', '8', ['systemd-portabled'], 'ENABLE_PORTABLED'],
- ['systemd-pstore', '8', ['systemd-pstore.service'], 'ENABLE_PSTORE'],
+ ['systemd-pstore.service', '8', ['systemd-pstore'], 'ENABLE_PSTORE'],
  ['systemd-quotacheck.service',
   '8',
   ['systemd-quotacheck'],
@@ -769,6 +787,7 @@ manpages = [
   'ENABLE_RANDOMSEED'],
  ['systemd-rc-local-generator', '8', [], ''],
  ['systemd-remount-fs.service', '8', ['systemd-remount-fs'], ''],
+ ['systemd-repart', '8', ['systemd-repart.service'], 'ENABLE_REPART'],
  ['systemd-resolved.service', '8', ['systemd-resolved'], 'ENABLE_RESOLVE'],
  ['systemd-rfkill.service',
   '8',
@@ -821,6 +840,7 @@ manpages = [
   ['systemd-update-utmp', 'systemd-update-utmp-runlevel.service'],
   'ENABLE_UTMP'],
  ['systemd-user-sessions.service', '8', ['systemd-user-sessions'], 'HAVE_PAM'],
+ ['systemd-userdbd.service', '8', ['systemd-userdbd'], 'ENABLE_USERDB'],
  ['systemd-vconsole-setup.service',
   '8',
   ['systemd-vconsole-setup'],
@@ -952,6 +972,7 @@ manpages = [
  ['udev_new', '3', ['udev_ref', 'udev_unref'], ''],
  ['udevadm', '8', [], ''],
  ['user@.service', '5', ['user-runtime-dir@.service'], ''],
+ ['userdbctl', '1', [], 'ENABLE_USERDB'],
  ['vconsole.conf', '5', [], 'ENABLE_VCONSOLE']
 ]
 # Really, do not edit.
index e9a66d87ddda2e438e872cd5d3492e4c172fc720..76865e1f8ee671c99e3eba6aea10857d6196f149 100644 (file)
@@ -58,6 +58,7 @@
 <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_copy</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_dump</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_get_cookie</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_get_monotonic_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_get_signature</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
diff --git a/man/sd_bus_enqueue_for_read.xml b/man/sd_bus_enqueue_for_read.xml
new file mode 100644 (file)
index 0000000..3318a30
--- /dev/null
@@ -0,0 +1,88 @@
+<?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_enqueue_for_read"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_enqueue_for_read</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_enqueue_for_read</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_enqueue_for_read</refname>
+
+    <refpurpose>Re-enqueue a bus message on a bus connection, for reading.</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_enqueue_for_read</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+      </funcprototype>
+
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_enqueue_for_read()</function> may be used to re-enqueue an incoming bus message on
+    the local read queue, so that it is processed and dispatched locally again, similar to how an incoming
+    message from the peer is processed. Takes a bus connection object and the message to enqueue. A reference
+    is taken of the message and the caller's reference thus remains in possession of the caller. The message
+    is enqueued at the end of the queue, thus will be dispatched after all other already queued messages are
+    dispatched.</para>
+
+    <para>This call is primarily useful for dealing with incoming method calls that may be processed only
+    after an additional asynchronous operation completes. One example are PolicyKit authorization requests
+    that are determined to be necessary to authorize a newly incoming method call: when the PolicyKit response
+    is received the original method call may be re-enqueued to process it again, this time with the
+    authorization result known.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, this function return 0 or a positive 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>-ECHILD</constant></term>
+
+          <listitem><para>The bus connection has been 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_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    </para>
+  </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_message_dump.xml b/man/sd_bus_message_dump.xml
new file mode 100644 (file)
index 0000000..db9e46d
--- /dev/null
@@ -0,0 +1,107 @@
+<?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_dump"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_message_dump</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_message_dump</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_message_dump</refname>
+
+    <refpurpose>Produce a string representation of a message for debugging purposes</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int sd_bus_message_dump</funcdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+        <paramdef>FILE *<parameter>f</parameter></paramdef>
+        <paramdef>uint64_t <parameter>flags</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+
+    <para>
+      <constant>SD_BUS_MESSAGE_DUMP_WITH_HEADER</constant>,
+      <constant>SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY</constant>
+    </para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>The <function>sd_bus_message_dump()</function> function writes a textual representation of the
+    message <parameter>m</parameter> to the stream <parameter>f</parameter>. This function is intended to be
+    used for debugging purposes, and the output is neither stable nor designed to be machine readable.
+    </para>
+
+    <para>The <parameter>flags</parameter> parameter may be used to modify the output. With
+    <constant>SD_BUS_MESSAGE_DUMP_WITH_HEADER</constant>, a header that specifies the message type and flags
+    and some additional metadata is printed. When <constant>SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY</constant> is
+    not passed, the contents of the whole message are printed. When it <emphasis>is</emphasis> passed,
+    only the current container in printed.</para>
+
+    <para>Note that this function moves the read pointer of the message. It may be necessary to reset the
+    position afterwards, for example with
+    <citerefentry><refentrytitle>sd_bus_message_rewind</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <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
+  Path=/value/a  Interface=org.freedesktop.DBus.Properties  Member=PropertiesChanged
+  MESSAGE "sa{sv}as" {
+          STRING "org.freedesktop.systemd.ValueTest";
+          ARRAY "{sv}" {
+                  DICT_ENTRY "sv" {
+                          STRING "Value";
+                          VARIANT "s" {
+                                  STRING "object 0x1e, path /value/a";
+                          };
+                  };
+          };
+          ARRAY "s" {
+                  STRING "Value2";
+                  STRING "AnExplicitProperty";
+          };
+  };
+    </programlisting>
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, this function returns 0 or a positive integer. On failure, it returns a negative
+    errno-style error code. No error codes are currently defined.</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 526fb0e70afec161b380b748f20af762b3dc2265..2afa44174229bba1e687e418bd13508462f790d4 100644 (file)
 
     <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 <code>int8_t</code> for a <literal>y</literal> in
-    the type string, a pointer to <code>int32_t</code> for an <literal>i</literal>, a pointer to
-    <code>const char*</code> for an <literal>s</literal>, ...)  which are set based on the values in
+    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., <code>const char *</code> in the case of a string), the argument is a pointer to a
+    (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>
 
@@ -99,7 +99,7 @@
             <entry><literal>a</literal></entry>
             <entry><constant>SD_BUS_TYPE_ARRAY</constant></entry>
             <entry>array</entry>
-            <entry>int, which specifies the expected length <parameter>n</parameter> of the array</entry>
+            <entry><type>int</type>, which specifies the expected length <parameter>n</parameter> of the array</entry>
             <entry><parameter>n</parameter> sets of arguments appropriate for the array element type</entry>
           </row>
 
@@ -174,6 +174,14 @@ int64_t x;
 
 sd_bus_message_read(m, "x", &amp;x);</programlisting>
 
+    <para>Read a boolean value:</para>
+
+    <programlisting>sd_bus_message *m;
+int x; /* Do not use C99 'bool' type here, it's typically smaller
+          in memory and would cause memory corruption */
+
+sd_bus_message_read(m, "b", &amp;x);</programlisting>
+
     <para>Read all types of integers:</para>
 
     <programlisting>uint8_t y;
index 117afa9e31e199483b28585454dcbd8aaa86935d..26e8ebae60b737b7c0ae8537da00c39d9a03b7b8 100644 (file)
     appropriate for the data type. The data is part of the message — it may not be modified and is
     valid only as long as the message is referenced. After this function returns, the "read pointer"
     points at the next element after the array.</para>
+
+    <para>Note that this function only supports arrays of trivial types, i.e. arrays of booleans, the various
+    integer types, as well as floating point numbers. In particular it may not be used for arrays of strings,
+    structures or similar.</para>
   </refsect1>
 
   <refsect1>
@@ -68,8 +72,8 @@
         <varlistentry>
           <term><constant>-EINVAL</constant></term>
 
-          <listitem><para>Specified type is invalid or the message parameter or one of the output
-          parameters are <constant>NULL</constant>.</para></listitem>
+          <listitem><para>Specified type is invalid or not a trivial type (see above), or the message
+          parameter or one of the output parameters are <constant>NULL</constant>.</para></listitem>
         </varlistentry>
 
         <varlistentry>
index 101f5d21a4998bf78cebf19314d8faa60ef85d55..e1e993434ac3f5a8b7dd2855b14a8a5c16314ec4 100644 (file)
       If <parameter>p</parameter> is not <constant>NULL</constant>, it should contain
       a pointer to an appropriate object. For example, if <parameter>type</parameter>
       is <constant>'y'</constant>, the object passed in <parameter>p</parameter>
-      should have type <code>uint8_t *</code>. If <parameter>type</parameter> is
+      should have type <type>uint8_t *</type>. If <parameter>type</parameter> is
       <constant>'s'</constant>, the object passed in <parameter>p</parameter> should
-      have type <code>const char **</code>. Note that, if the basic type is a pointer
-      (e.g., <code>const char *</code> in the case of a string), the pointer is only
+      have type <type>const char **</type>. Note that, if the basic type is a pointer
+      (e.g., <type>const char *</type> in the case of a string), the pointer is only
       borrowed and the contents must be copied if they are to be used after the end
       of the messages lifetime. Similarly, during the lifetime of such a pointer, the
       message must not be modified. See the table below for a complete list of allowed
           <row>
             <entry><literal>y</literal></entry>
             <entry><constant>SD_BUS_TYPE_BYTE</constant></entry>
-            <entry>unsigned integer</entry>
-            <entry>uint8_t *</entry>
+            <entry>8bit unsigned integer</entry>
+            <entry><type>uint8_t *</type></entry>
           </row>
 
           <row>
             <entry><literal>b</literal></entry>
             <entry><constant>SD_BUS_TYPE_BOOLEAN</constant></entry>
             <entry>boolean</entry>
-            <entry>int *</entry>
+            <entry><type>int *</type> (NB: not <type>bool *</type>)</entry>
           </row>
 
           <row>
             <entry><literal>n</literal></entry>
             <entry><constant>SD_BUS_TYPE_INT16</constant></entry>
-            <entry>signed integer</entry>
-            <entry>int16_t *</entry>
+            <entry>16bit signed integer</entry>
+            <entry><type>int16_t *</type></entry>
           </row>
 
           <row>
             <entry><literal>q</literal></entry>
             <entry><constant>SD_BUS_TYPE_UINT16</constant></entry>
-            <entry>unsigned integer</entry>
-            <entry>uint16_t *</entry>
+            <entry>16bit unsigned integer</entry>
+            <entry><type>uint16_t *</type></entry>
           </row>
 
           <row>
             <entry><literal>i</literal></entry>
             <entry><constant>SD_BUS_TYPE_INT32</constant></entry>
-            <entry>signed integer</entry>
-            <entry>int32_t *</entry>
+            <entry>32bit signed integer</entry>
+            <entry><type>int32_t *</type></entry>
           </row>
 
           <row>
             <entry><literal>u</literal></entry>
             <entry><constant>SD_BUS_TYPE_UINT32</constant></entry>
-            <entry>unsigned integer</entry>
-            <entry>uint32_t *</entry>
+            <entry>32bit unsigned integer</entry>
+            <entry><type>uint32_t *</type></entry>
           </row>
 
           <row>
             <entry><literal>x</literal></entry>
             <entry><constant>SD_BUS_TYPE_INT64</constant></entry>
-            <entry>signed integer</entry>
-            <entry>int64_t *</entry>
+            <entry>64bit signed integer</entry>
+            <entry><type>int64_t *</type></entry>
           </row>
 
           <row>
             <entry><literal>t</literal></entry>
             <entry><constant>SD_BUS_TYPE_UINT64</constant></entry>
-            <entry>unsigned integer</entry>
-            <entry>uint64_t *</entry>
+            <entry>64bit unsigned integer</entry>
+            <entry><type>uint64_t *</type></entry>
           </row>
 
           <row>
             <entry><literal>d</literal></entry>
             <entry><constant>SD_BUS_TYPE_DOUBLE</constant></entry>
-            <entry>floating-point</entry>
-            <entry>double *</entry>
+            <entry>IEEE 754 double precision floating-point</entry>
+            <entry><type>double *</type></entry>
           </row>
 
           <row>
             <entry><literal>s</literal></entry>
             <entry><constant>SD_BUS_TYPE_STRING</constant></entry>
-            <entry>Unicode string</entry>
-            <entry>const char **</entry>
+            <entry>UTF-8 string</entry>
+            <entry><type>const char **</type></entry>
           </row>
 
           <row>
             <entry><literal>o</literal></entry>
             <entry><constant>SD_BUS_TYPE_OBJECT_PATH</constant></entry>
-            <entry>object path</entry>
-            <entry>const char **</entry>
+            <entry>D-Bus object path string</entry>
+            <entry><type>const char **</type></entry>
           </row>
 
           <row>
             <entry><literal>g</literal></entry>
             <entry><constant>SD_BUS_TYPE_SIGNATURE</constant></entry>
-            <entry>signature</entry>
-            <entry>const char **</entry>
+            <entry>D-Bus signature string</entry>
+            <entry><type>const char **</type></entry>
           </row>
 
           <row>
             <entry><literal>h</literal></entry>
             <entry><constant>SD_BUS_TYPE_UNIX_FD</constant></entry>
             <entry>UNIX file descriptor</entry>
-            <entry>int *</entry>
+            <entry><type>int *</type></entry>
           </row>
        </tbody>
       </tgroup>
index 47e57107cd0412a2c030a39f57c7292533647f4c..9884beae1a348718b9a3aec64cb5025b873c7a01 100644 (file)
@@ -29,6 +29,8 @@
     <refname>SD_JOURNAL_SYSTEM</refname>
     <refname>SD_JOURNAL_CURRENT_USER</refname>
     <refname>SD_JOURNAL_OS_ROOT</refname>
+    <refname>SD_JOURNAL_ALL_NAMESPACES</refname>
+    <refname>SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE</refname>
     <refpurpose>Open the system journal for reading</refpurpose>
   </refnamediv>
 
         <paramdef>int <parameter>flags</parameter></paramdef>
       </funcprototype>
 
+      <funcprototype>
+        <funcdef>int <function>sd_journal_open_namespace</function></funcdef>
+        <paramdef>sd_journal **<parameter>ret</parameter></paramdef>
+        <paramdef>const char *<parameter>namespace</parameter></paramdef>
+        <paramdef>int <parameter>flags</parameter></paramdef>
+      </funcprototype>
+
       <funcprototype>
         <funcdef>int <function>sd_journal_open_directory</function></funcdef>
         <paramdef>sd_journal **<parameter>ret</parameter></paramdef>
     <constant>SD_JOURNAL_CURRENT_USER</constant> are specified, all
     journal file types will be opened.</para>
 
+    <para><function>sd_journal_open_namespace()</function> is similar to
+    <function>sd_journal_open()</function> but takes an additional <parameter>namespace</parameter> parameter
+    that specifies which journal namespace to operate on. If specified as <constant>NULL</constant> the call
+    is identical to <function>sd_journal_open()</function>. If non-<constant>NULL</constant> only data from
+    the namespace identified by the specified parameter is accessed. This call understands two additional
+    flags: if <constant>SD_JOURNAL_ALL_NAMESPACES</constant> is specified the
+    <parameter>namespace</parameter> parameter is ignored and all defined namespaces are accessed
+    simultaneously; if <constant>SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE</constant> the specified namespace and
+    the default namespace are accessed but no others (this flag has no effect when
+    <parameter>namespace</parameter> is passed as <constant>NULL</constant>). For details about journal
+    namespaces see
+    <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
     <para><function>sd_journal_open_directory()</function> is similar to <function>sd_journal_open()</function> but
     takes an absolute directory path as argument. All journal files in this directory will be opened and interleaved
     automatically. This call also takes a flags argument. The flags parameters accepted by this call are
index 1db859ac2fed4b4a8d0c5c0a9e978246339e2e6f..a58c76d85fa7da19863361beaf8f05bfd6b9eddc 100644 (file)
   <refsection id='confd'>
     <title>Configuration Directories and Precedence</title>
 
-    <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.  Each
-    configuration file in these configuration directories shall be named in the style of
-    <filename><replaceable>filename</replaceable>.conf</filename>.  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 under
-    <filename>/usr/</filename>.</para>
+    <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
+    <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
+    under <filename>/usr/</filename>.</para>
 
-    <para>Packages should install their configuration files in <filename>/usr/lib/</filename> (distribution packages)
-    or <filename>/usr/local/lib/</filename> (local installs). Files in <filename>/etc/</filename> are
-    reserved for the local administrator, who may use this logic to override the
-    configuration files installed by vendor packages. All configuration files
-    are sorted by their filename in lexicographic order, regardless of which of
-    the directories they reside in. If multiple files specify the same option,
-    the entry in the file with the lexicographically latest name will take
-    precedence. It is recommended to prefix all filenames with a two-digit number
-    and a dash, to simplify the ordering of the files.</para>
+    <para>All configuration files are sorted by their filename in lexicographic order, regardless of which of
+    the directories they reside in. If multiple files specify the same option, the entry in the file with the
+    lexicographically latest name will take precedence. Thus, the configuration in a certain file may either
+    be replaced completely (by placing a file with the same name in a directory with higher priority), or
+    individual settings might be changed (by specifying additional settings in a file with a different name
+    that is ordered later).</para>
 
-    <para>If the administrator wants to disable a configuration file supplied by
-    the vendor, the recommended way is to place a symlink to
-    <filename>/dev/null</filename> in the configuration directory in
-    <filename>/etc/</filename>, with the same filename as the vendor
-    configuration file. If the vendor configuration file is included in
-    the initrd image, the image has to be regenerated.</para>
+    <para>Packages should install their configuration files in <filename>/usr/lib/</filename> (distribution
+    packages) or <filename>/usr/local/lib/</filename> (local installs). Files in <filename>/etc/</filename>
+    are reserved for the local administrator, who may use this logic to override the configuration files
+    installed by vendor packages. It is recommended to prefix all filenames with a two-digit number and a
+    dash, to simplify the ordering of the files.</para>
+
+    <para>If the administrator wants to disable a configuration file supplied by the vendor, the recommended
+    way is to place a symlink to <filename>/dev/null</filename> in the configuration directory in
+    <filename>/etc/</filename>, with the same filename as the vendor configuration file. If the vendor
+    configuration file is included in the initrd image, the image has to be regenerated.</para>
   </refsection>
 
   <refsection id='main-conf'>
     can be edited to create local overrides.
     </para>
 
-    <para>When packages need to customize the configuration, they can
-    install configuration snippets in
-    <filename>/usr/lib/systemd/*.conf.d/</filename> or
-    <filename>/usr/local/lib/systemd/*.conf.d/</filename>. Files in
-    <filename>/etc/</filename> are reserved for the local
-    administrator, who may use this logic to override the
-    configuration files installed by vendor packages. The main
-    configuration file is read before any of the configuration
-    directories, and has the lowest precedence; entries in a file in
-    any configuration directory override entries in the single
-    configuration file. Files in the <filename>*.conf.d/</filename>
-    configuration subdirectories are sorted by their filename in lexicographic
-    order, regardless of which of the subdirectories they reside in. When
-    multiple files specify the same option, for options which accept just a
-    single value, the entry in the file with the lexicographically latest name
-    takes precedence. For options which accept a list of values, entries are
-    collected as they occur in files sorted lexicographically. It is recommended
-    to prefix all filenames in those subdirectories with a two-digit number and
-    a dash, to simplify the ordering of the files.</para>
+    <para>When packages need to customize the configuration, they can install configuration snippets in
+    <filename>/usr/lib/systemd/*.conf.d/</filename> or <filename>/usr/local/lib/systemd/*.conf.d/</filename>.
+    The main configuration file is read before any of the configuration directories, and has the lowest
+    precedence; entries in a file in any configuration directory override entries in the single configuration
+    file. Files in the <filename>*.conf.d/</filename> configuration subdirectories are sorted by their
+    filename in lexicographic order, regardless of in which of the subdirectories they reside. When multiple
+    files specify the same option, for options which accept just a single value, the entry in the file with
+    the lexicographically latest name takes precedence. For options which accept a list of values, entries
+    are collected as they occur in files sorted lexicographically.</para>
+
+    <para>Files in <filename>/etc/</filename> are reserved for the local administrator, who may use this
+    logic to override the configuration files installed by vendor packages. It is recommended to prefix all
+    filenames in those subdirectories with a two-digit number and a dash, to simplify the ordering of the
+    files.</para>
 
     <para>To disable a configuration file supplied by the vendor, the
     recommended way is to place a symlink to
diff --git a/man/supported-controllers.xml b/man/supported-controllers.xml
new file mode 100644 (file)
index 0000000..15e048b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>
+
+<para id="controllers-text">The following controller names may be specified: <option>cpu</option>, <option>cpuacct</option>,
+<option>cpuset</option>, <option>io</option>, <option>blkio</option>, <option>memory</option>, <option>devices</option>,
+<option>pids</option>, <option>bpf-firewall</option>, and <option>bpf-devices</option>.</para>
+
+</refsect1>
index 32084ee8d6b81d37b064b79edf482d6d0cb058f6..4df71e21eb920e8c13468cdba9e462a353928ff2 100644 (file)
     <para><filename>/etc/sysctl.d/*.conf</filename></para>
     <para><filename>/run/sysctl.d/*.conf</filename></para>
     <para><filename>/usr/lib/sysctl.d/*.conf</filename></para>
+
+    <programlisting>key.name.under.proc.sys = some value
+key/name/under/proc/sys = some value
+key/middle.part.with.dots/foo = 123
+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
+</programlisting>
   </refsynopsisdiv>
 
   <refsect1>
     first non-whitespace character is <literal>#</literal> or
     <literal>;</literal> are ignored.</para>
 
-    <para>Note that either <literal>/</literal> or
-    <literal>.</literal> may be used as separators within sysctl
-    variable names. If the first separator is a slash, remaining
-    slashes and dots are left intact. If the first separator is a dot,
-    dots and slashes are interchanged.
-    <literal>kernel.domainname=foo</literal> and
-    <literal>kernel/domainname=foo</literal> are equivalent and will
-    cause <literal>foo</literal> to be written to
+    <para>Note that either <literal>/</literal> or <literal>.</literal> may be used as separators within
+    sysctl variable names. If the first separator is a slash, remaining slashes and dots are left intact. If
+    the first separator is a dot, dots and slashes are interchanged.
+    <literal>kernel.domainname=foo</literal> and <literal>kernel/domainname=foo</literal> are equivalent and
+    will cause <literal>foo</literal> to be written to
     <filename>/proc/sys/kernel/domainname</filename>. Either
     <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>.
-    </para>
-
-    <para>Any access permission errors and attempts to write variables not defined on the local system are
-    logged, but 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 will be logged, but will not cause the
-    service to fail. All other errors when setting variables cause the service to return failure at the end
-    (other variables are still processed).</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 shows up in the system. (More
-    specifically, <filename>net.ipv4.conf.*</filename>,
-    <filename>net.ipv6.conf.*</filename>,
-    <filename>net.ipv4.neigh.*</filename> and
+    <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
+    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>
+
+    <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
+    shows up in the system. (More specifically, <filename>net.ipv4.conf.*</filename>,
+    <filename>net.ipv6.conf.*</filename>, <filename>net.ipv4.neigh.*</filename> and
     <filename>net.ipv6.neigh.*</filename>).</para>
 
     <para>Many sysctl parameters only become available when certain
@@ -148,6 +156,26 @@ net.bridge.bridge-nf-call-arptables = 0
       (starting with kernel 3.18), so simply not loading the module is
       sufficient to avoid filtering.</para>
     </example>
+
+    <example>
+      <title>Set network routing properties for all interfaces</title>
+      <para><filename>/etc/systemd/20-rp_filter.conf</filename>:</para>
+
+      <programlisting>net.ipv4.conf.default.rp_filter = 2
+net.ipv4.conf.*.rp_filter = 2
+-net.ipv4.conf.all.rp_filter
+net.ipv4.conf.hub0.rp_filter = 1
+</programlisting>
+
+      <para>The <option>rp_filter</option> key will be set to "2" for all interfaces, except "hub0". We set
+      <filename>net.ipv4.conf.default.rp_filter</filename> first, so any interfaces which are added
+      <emphasis>later</emphasis> will get this value (this also covers any interfaces detected while we're
+      running). The glob matches any interfaces which were detected <emphasis>earlier</emphasis>. The glob
+      will also match <filename>net.ipv4.conf.all.rp_filter</filename>, which we don't want to set at all, so
+      it is explicitly excluded. And "hub0" is excluded from the glob because it has an explicit setting.
+      </para>
+    </example>
+
   </refsect1>
 
   <refsect1>
index ceec7b0479d4c04492513ec403b4ea1cb389d920..e8d5f9f4d8e2b16cfb9e2b3efeb08887974b1121 100644 (file)
@@ -507,17 +507,17 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         <varlistentry>
           <term>
             <command>list-dependencies</command>
-            <optional><replaceable>UNIT</replaceable></optional>
+            <optional><replaceable>UNIT</replaceable>...</optional>
           </term>
 
           <listitem>
             <para>Shows units required and wanted by the specified
-            unit. This recursively lists units following the
+            units. This recursively lists units following the
             <varname>Requires=</varname>,
             <varname>Requisite=</varname>,
             <varname>ConsistsOf=</varname>,
             <varname>Wants=</varname>, <varname>BindsTo=</varname>
-            dependencies. If no unit is specified,
+            dependencies. If no units are specified,
             <filename>default.target</filename> is implied.</para>
 
             <para>By default, only target units are recursively
index cd5e9ac019ac75635d1ef5be489cf6d59361fd7e..4a45560a8cddeab8c3e253f5b383bd6c346e9a00 100644 (file)
@@ -18,6 +18,7 @@
 
   <refnamediv>
     <refname>systemd-bless-boot.service</refname>
+    <refname>systemd-bless-boot</refname>
     <refpurpose>Mark current boot process as successful</refpurpose>
   </refnamediv>
 
index d6d587f9ca50ea07c0332e92c6309ca1b122c5fc..8509fa633ed0ee1f32a06b68b613077ffe80bd0b 100644 (file)
@@ -18,6 +18,7 @@
 
   <refnamediv>
     <refname>systemd-boot-check-no-failures.service</refname>
+    <refname>systemd-boot-check-no-failures</refname>
     <refpurpose>verify that the system booted up cleanly</refpurpose>
   </refnamediv>
 
index 82321c44bf85a4a692ba6a6e2c1f1c18e51d52f9..ab332588581b9c1ee39cefbe0591f276eb926b0e 100644 (file)
       <varlistentry>
         <term><varname>rootflags=</varname></term>
 
-        <listitem><para>Takes the root filesystem mount options to
-        use. <varname>rootflags=</varname> is honored by the
-        initrd.</para></listitem>
+        <listitem><para>Takes the root filesystem mount options to use. <varname>rootflags=</varname> is
+        honored by the initrd.</para>
+
+        <para>Note that unlike most kernel command line options this setting does not override settings made
+        in configuration files (specifically: the mount option string in
+        <filename>/etc/fstab</filename>). See
+        <citerefentry><refentrytitle>systemd-remount-fs.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 0b9d88a87a64111981744c7961436e844d5d7c22..b0fa617d637899aabde433fdf129b3badbd5a955 100644 (file)
 
   <refnamediv>
     <refname>systemd-gpt-auto-generator</refname>
-    <refpurpose>Generator for automatically discovering
-    and mounting root, <filename>/home</filename> and
-    <filename>/srv</filename> partitions, as well as
-    discovering and enabling swap partitions, based on GPT
-    partition type GUIDs.</refpurpose>
+    <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>
   </refnamediv>
 
   <refsynopsisdiv>
     <title>Description</title>
 
     <para><filename>systemd-gpt-auto-generator</filename> is a unit generator that automatically discovers
-    root, <filename>/home/</filename>, <filename>/srv/</filename>, the EFI System Partition, the Extended
-    Boot Loader Partition and swap partitions and creates mount and swap units for them, based on the
-    partition type GUIDs of GUID partition tables (GPT), see <ulink
-    url="https://uefi.org/specifications">UEFI Specification</ulink>, chapter 5. It implements the <ulink
-    url="https://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/">Discoverable Partitions
+    root, <filename>/home/</filename>, <filename>/srv/</filename>, <filename>/var/</filename>,
+    <filename>/var/tmp/</filename>, the EFI System Partition, the Extended Boot Loader Partition and swap
+    partitions and creates mount and swap units for them, based on the partition type GUIDs of GUID partition
+    tables (GPT), see <ulink url="https://uefi.org/specifications">UEFI Specification</ulink>, chapter 5. It
+    implements the <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
     Specification</ulink>. Note that this generator has no effect on non-GPT systems, and on specific mount
     points that are directories already containing files. Also, on systems where the units are explicitly
     configured (for example, listed in <citerefentry
     created.</para>
 
     <para>This generator will only look for the root partition on the same physical disk the EFI System
-    Partition (ESP) is located on. Note that support from the boot loader is required: EFI variable
-    <varname>LoaderDevicePartUUID-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f</varname> is used to determine from
-    which partition, and hence the disk from which the system was booted. If the boot loader does not set
-    this variable, this generator will not be able to autodetect the root partition.</para>
+    Partition (ESP) is located on. Note that support from the boot loader is required: the EFI variable
+    <varname>LoaderDevicePartUUID</varname> of the <constant>4a67b082-0a4c-41cf-b6c7-440b29bb8c4f</constant>
+    vendor UUID is used to determine from which partition, and hence the disk from which the system was
+    booted. If the boot loader does not set this variable, this generator will not be able to autodetect the
+    root partition. See the <ulink url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader
+    Interface</ulink> for details.</para>
 
     <para>Similarly, this generator will only look for the other partitions on the same physical disk as the
     root partition. In this case, boot loader support is not required. These partitions will not be searched
             <entry>933ac7e1-2eb4-4f13-b844-0e14e2aef915</entry>
             <entry>Home Partition</entry>
             <entry><filename>/home/</filename></entry>
-            <entry>The first home partition on the disk the root partition is located on is mounted to <filename>/home</filename>.</entry>
+            <entry>The first home partition on the disk the root partition is located on is mounted to <filename>/home/</filename>.</entry>
           </row>
           <row>
             <entry>3b8f8425-20e0-4f3b-907f-1a25a76f98e8</entry>
             <entry>Server Data Partition</entry>
             <entry><filename>/srv/</filename></entry>
-            <entry>The first server data partition on the disk the root partition is located on is mounted to <filename>/srv</filename>.</entry>
+            <entry>The first server data partition on the disk the root partition is located on is mounted to <filename>/srv/</filename>.</entry>
+          </row>
+          <row>
+            <entry>4d21b016-b534-45c2-a9fb-5c16e091fd2d</entry>
+            <entry>Variable Data Partition</entry>
+            <entry><filename>/var/</filename></entry>
+            <entry>The first variable data partition on the disk the root partition is located on is mounted to <filename>/var/</filename> — under the condition its partition UUID matches the first 128 bit of the HMAC-SHA256 of the GPT type uuid of this partition keyed by the machine ID of the installation stored in <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</entry>
+          </row>
+          <row>
+            <entry>7ec6f557-3bc5-4aca-b293-16ef5df639d1</entry>
+            <entry>Temporary Data Partition</entry>
+            <entry><filename>/var/tmp/</filename></entry>
+            <entry>The first temporary data partition on the disk the root partition is located on is mounted to <filename>/var/tmp/</filename>.</entry>
           </row>
           <row>
             <entry>0657fd6d-a4ab-43c4-84e5-0933c84b4f4f</entry>
             <entry>c12a7328-f81f-11d2-ba4b-00a0c93ec93b</entry>
             <entry>EFI System Partition (ESP)</entry>
             <entry><filename>/efi/</filename> or <filename>/boot/</filename></entry>
-            <entry>The first ESP located on the disk the root partition is located on is mounted to <filename>/boot</filename> or <filename>/efi</filename>, see below.</entry>
+            <entry>The first ESP located on the disk the root partition is located on is mounted to <filename>/boot/</filename> or <filename>/efi/</filename>, see below.</entry>
           </row>
           <row>
             <entry>bc13c2ff-59e6-4262-a352-b275fd6f7172</entry>
             <entry>Extended Boot Loader Partition</entry>
             <entry><filename>/boot/</filename></entry>
-            <entry>The first Extended Boot Loader Partition is mounted to <filename>/boot</filename>, see below.</entry>
+            <entry>The first Extended Boot Loader Partition is mounted to <filename>/boot/</filename>, see below.</entry>
           </row>
         </tbody>
       </tgroup>
           <row>
             <entry><constant>GPT_FLAG_READ_ONLY</constant></entry>
             <entry>0x1000000000000000</entry>
-            <entry><filename>/</filename>, <filename>/home/</filename>, <filename>/srv/</filename>, Extended Boot Loader Partition</entry>
+            <entry><filename>/</filename>, <filename>/home/</filename>, <filename>/srv/</filename>, <filename>/var/</filename>, <filename>/var/tmp/</filename>, Extended Boot Loader Partition</entry>
             <entry>Partition is mounted read-only</entry>
           </row>
 
           <row>
             <entry><constant>GPT_FLAG_NO_AUTO</constant></entry>
             <entry>0x8000000000000000</entry>
-            <entry><filename>/</filename>, <filename>/home/</filename>, <filename>/srv/</filename>, Extended Boot Loader Partition</entry>
+            <entry><filename>/</filename>, <filename>/home/</filename>, <filename>/srv/</filename>, <filename>/var/</filename>, <filename>/var/tmp/</filename>, Extended Boot Loader Partition</entry>
             <entry>Partition is not mounted automatically</entry>
           </row>
 
       </tgroup>
     </table>
 
-    <para>The <filename>/home/</filename> and <filename>/srv/</filename> partitions may be encrypted in LUKS
-    format. In this case, a device mapper device is set up under the names
-    <filename>/dev/mapper/home</filename> and <filename>/dev/mapper/srv</filename>. Note that this might
-    create conflicts if the same partition is listed in <filename>/etc/crypttab</filename> with a different
-    device mapper device name.</para>
+    <para>The <filename>/home/</filename>, <filename>/srv/</filename>, <filename>/var/</filename> and
+    <filename>/var/tmp/</filename> partitions may be encrypted in LUKS format. In this case, a device mapper
+    device is set up under the names <filename>/dev/mapper/home</filename>,
+    <filename>/dev/mapper/srv</filename>, <filename>/dev/mapper/var</filename> and
+    <filename>/dev/mapper/tmp</filename>. Note that this might create conflicts if the same partition is
+    listed in <filename>/etc/crypttab</filename> with a different device mapper device name.</para>
 
     <para>When systemd is running in the initrd the <filename>/</filename> partition may be encrypted in LUKS
       format as well. In this case, a device mapper device is set up under the name <filename>/dev/mapper/root</filename>,
 
     <para>If the disk contains an Extended Boot Loader partition, as defined in the <ulink
     url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>, it is made
-    available at <filename>/boot</filename> (by means of an automount point, similar to the ESP, see
+    available at <filename>/boot/</filename> (by means of an automount point, similar to the ESP, see
     above). If both an EFI System Partition and an Extended Boot Loader partition exist the latter is
     preferably mounted to <filename>/boot/</filename>. Make sure to create both <filename>/efi/</filename>
     and <filename>/boot/</filename> to ensure both partitions are mounted.</para>
       <citerefentry><refentrytitle>systemd.swap</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry project='die-net'><refentrytitle>cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry project='man-pages'><refentrytitle>btrfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>
diff --git a/man/systemd-homed.service.xml b/man/systemd-homed.service.xml
new file mode 100644 (file)
index 0000000..26789a2
--- /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-homed.service" conditional='ENABLE_HOMED'>
+
+  <refentryinfo>
+    <title>systemd-homed.service</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-homed.service</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-homed.service</refname>
+    <refname>systemd-homed</refname>
+    <refpurpose>Home Directory/User Account Manager</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <para><filename>systemd-homed.service</filename></para>
+    <para><filename>/usr/lib/systemd/systemd-homed</filename></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>systemd-homed</command> is a system service that may be used to create, remove, change or
+    inspect home directories.</para>
+
+    <para>Most of <command>systemd-homed</command>'s functionality is accessible through the
+    <citerefentry><refentrytitle>homectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> command.</para>
+
+    <para>See the <ulink url="https://systemd.io/HOME_DIRECTORY">Home Directories</ulink> documentation for
+    details about the format and design of home directories managed by
+    <filename>systemd-homed.service</filename>.</para>
+
+    <para>Each home directory managed by <filename>systemd-homed.service</filename> synthesizes a local user
+    and group. 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
+    browsed with
+    <citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>homectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+</refentry>
index a5ab31ad6dfe5276e0e2221528a952fd19e14e67..747b703653209cc1f9df20d9f15eaea9464b4f6e 100644 (file)
     will be printed. This is available in systemd services. See
     <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
     </para>
+
+    <para>With <command>show</command>, well-known UUIDs are printed. When no arguments are specified, all
+    known UUIDs are shown. When arguments are specified, they must be the names or values of one or more
+    known UUIDs, which are then printed.</para>
   </refsect1>
 
   <refsect1>
index ec9f974f2da5ea7ba164253b882a726fd1cb59ab..f4ce4e4fe2079ac8d58461f3e54af71f3f33165d 100644 (file)
@@ -20,6 +20,9 @@
     <refname>systemd-journald.socket</refname>
     <refname>systemd-journald-dev-log.socket</refname>
     <refname>systemd-journald-audit.socket</refname>
+    <refname>systemd-journald@.service</refname>
+    <refname>systemd-journald@.socket</refname>
+    <refname>systemd-journald-varlink@.socket</refname>
     <refname>systemd-journald</refname>
     <refpurpose>Journal service</refpurpose>
   </refnamediv>
@@ -29,6 +32,9 @@
     <para><filename>systemd-journald.socket</filename></para>
     <para><filename>systemd-journald-dev-log.socket</filename></para>
     <para><filename>systemd-journald-audit.socket</filename></para>
+    <para><filename>systemd-journald@.service</filename></para>
+    <para><filename>systemd-journald@.socket</filename></para>
+    <para><filename>systemd-journald-varlink@.socket</filename></para>
     <para><filename>/usr/lib/systemd/systemd-journald</filename></para>
   </refsynopsisdiv>
 
@@ -129,6 +135,40 @@ systemd-tmpfiles --create --prefix /var/log/journal</programlisting>
     <constant>EPIPE</constant> right from the beginning.</para>
   </refsect1>
 
+  <refsect1>
+    <title>Journal Namespaces</title>
+
+    <para>Journal 'namespaces' are both a mechanism for logically isolating the log stream of projects
+    consisting of one or more services from the rest of the system and a mechanism for improving
+    performance. Multiple journal namespaces may exist simultaneously, each defining its own, independent log
+    stream managed by its own instance of <command>systemd-journald</command>. Namespaces are independent of
+    each other, both in the data store and in the IPC interface. By default only a single 'default' namespace
+    exists, managed by <filename>systemd-journald.service</filename> (and its associated socket
+    units). Additional namespaces are created by starting an instance of the
+    <filename>systemd-journald@.service</filename> service template. The instance name is the namespace
+    identifier, which is a short string used for referencing the journal namespace. Service units may be
+    assigned to a specific journal namespace through the <varname>LogNamespace=</varname> unit file setting,
+    see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+    details. The <option>--namespace=</option> switch of
+    <citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> may be
+    used to view the log stream of a specific namespace. If the switch is not used the log stream of the
+    default namespace is shown, i.e. log data from other namespaces is not visible.</para>
+
+    <para>Services associated with a specific log namespace may log via syslog, the native logging protocol
+    of the journal and via stdout/stderr; the logging from all three transports is associated with the
+    namespace.</para>
+
+    <para>By default only the default namespace will collect kernel and audit log messages.</para>
+
+    <para>The <command>systemd-journald</command> instance of the default namespace is configured through
+    <filename>/etc/systemd/journald.conf</filename> (see below), while the other instances are configured
+    through <filename>/etc/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf</filename>. The journal
+    log data for the default namespace is placed in
+    <filename>/var/log/journal/<replaceable>MACHINE_ID</replaceable></filename> (see below) while the data
+    for the other namespaces is located in
+    <filename>/var/log/journal/<replaceable>MACHINE_ID</replaceable>.<replaceable>NAMESPACE</replaceable></filename>.</para>
+  </refsect1>
+
   <refsect1>
     <title>Signals</title>
 
@@ -190,6 +230,9 @@ systemd-tmpfiles --create --prefix /var/log/journal</programlisting>
 
       </varlistentry>
     </variablelist>
+
+    <para>Note that these kernel command line options are only honoured by the default namespace, see
+    above.</para>
   </refsect1>
 
   <refsect1>
@@ -279,12 +322,14 @@ systemd-tmpfiles --create --prefix /var/log/journal</programlisting>
         <term><filename>/run/systemd/journal/socket</filename></term>
         <term><filename>/run/systemd/journal/stdout</filename></term>
 
-        <listitem><para>Sockets and other paths that
-        <command>systemd-journald</command> will listen on that are
-        visible in the file system. In addition to these, journald can
-        listen for audit events using netlink.</para></listitem>
+        <listitem><para>Sockets and other file node paths that <command>systemd-journald</command> will
+        listen on and are visible in the file system. In addition to these,
+        <command>systemd-journald</command> can listen for audit events using <citerefentry
+        project='man-pages'><refentrytitle>netlink</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para></listitem>
       </varlistentry>
     </variablelist>
+
+    <para>If journal namespacing is used these paths are slightly altered to include a namespace identifier, see above.</para>
   </refsect1>
 
   <refsect1>
@@ -296,7 +341,7 @@ systemd-tmpfiles --create --prefix /var/log/journal</programlisting>
       <citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd-journal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-coredump</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
-      <citerefentry project='die-net'><refentrytitle>setfacl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>setfacl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_journal_print</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <command>pydoc systemd.journal</command>
     </para>
index 8514af67bc4835a0fdd39cfc73672382611af9c5..d07d90315a4140e4c3ea117449d8dce2e8c1cbeb 100644 (file)
@@ -80,6 +80,7 @@
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry project='man-pages'><refentrytitle>mkfs.btrfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry project='man-pages'><refentrytitle>mkfs.cramfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry project='man-pages'><refentrytitle>mkfs.ext4</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
diff --git a/man/systemd-network-generator.service.xml b/man/systemd-network-generator.service.xml
new file mode 100644 (file)
index 0000000..fcb3c69
--- /dev/null
@@ -0,0 +1,103 @@
+<?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-network-generator.service" conditional='ENABLE_NETWORKD'>
+
+  <refentryinfo>
+    <title>systemd-network-generator.service</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-network-generator.service</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-network-generator.service</refname>
+    <refname>systemd-network-generator</refname>
+    <refpurpose>Generate network configuration from the kernel command line</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <para><filename>systemd-network-generator.service</filename></para>
+    <para><filename>/usr/lib/systemd/systemd-network-generator</filename></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><filename>systemd-network-generator.service</filename> is a system service that translates
+    <varname>ip=</varname> and the related settings on the kernel command line (see below) into
+    <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>, and
+    <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    configuration files understood by
+    <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    and
+    <citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+    </para>
+
+    <para>Files are generated in <filename>/run/systemd/network/</filename>.</para>
+  </refsect1>
+
+  <refsect1><title>Kernel command line options</title>
+  <para>This tool understands the following options:</para>
+
+    <variablelist class='kernel-commandline-options'>
+      <varlistentry>
+        <term><varname>ip=</varname></term>
+        <term><varname>rd.route=</varname></term>
+        <term><varname>rd.peerdns=</varname></term>
+        <listitem>
+          <para>— translated into
+          <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry> files.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>ifname=</varname></term>
+        <listitem>
+          <para>— translated into
+          <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry> files.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>vlan=</varname></term>
+        <term><varname>bond=</varname></term>
+        <term><varname>bridge=</varname></term>
+        <term><varname>bootdev=</varname></term>
+        <listitem>
+          <para>— translated into
+          <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry> files.</para>
+        </listitem>
+      </varlistentry>
+
+      <!-- unsupported:
+           team=<teammaster>:<teamslaves>
+           bootdev=
+           BOOTIF=
+           bootdev=
+           bootdev=
+           bootdev=
+      -->
+    </variablelist>
+
+    <para>See
+    <citerefentry project='man-pages'><refentrytitle>dracut.kernel</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+    for option syntax and details.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <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>
+    </para>
+  </refsect1>
+
+</refentry>
index 96fcb5fb48e3c58b125549a23ddf58f4f2b7793d..e2f1eb0e831b9c61478b078b2e11496b48d8139c 100644 (file)
 
     <variablelist>
       <varlistentry>
-        <term><option>-i</option> <replaceable>INTERFACE</replaceable><optional>:<replaceable>OPERSTATE</replaceable></optional></term>
-        <term><option>--interface=</option><replaceable>INTERFACE</replaceable><optional>:<replaceable>OPERSTATE</replaceable></optional></term>
+        <term><option>-i</option> <replaceable>INTERFACE</replaceable><optional>:<replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></optional></term>
+        <term><option>--interface=</option><replaceable>INTERFACE</replaceable><optional>:<replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></optional></term>
 
         <listitem><para>Network interface to wait for before deciding if the system is online. This
         is useful when a system has several interfaces which will be configured, but a particular
         one is necessary to access some network resources. When used, all other interfaces are ignored.
         This option may be used more than once to wait for multiple network interfaces. When this
         option is specified multiple times, then <command>systemd-networkd-wait-online</command> waits
-        for all specified interfaces to be online. Optionally, required minimum operational state can be
-        specified after a colon <literal>:</literal>. Please see
+        for all specified interfaces to be online. Optionally, required minimum and maximum operational
+        states can be specified after a colon <literal>:</literal>. Please see
         <citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
         for possible operational states. If the operational state is not specified here, then
         the value from <varname>RequiredForOnline=</varname> in the corresponding
       </varlistentry>
 
       <varlistentry>
-        <term><option>-o</option> <replaceable>OPERSTATE</replaceable></term>
-        <term><option>--operational-state=</option><replaceable>OPERSTATE</replaceable></term>
+        <term><option>-o</option> <replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></term>
+        <term><option>--operational-state=</option><replaceable>MIN_OPERSTATE</replaceable><optional>:<replaceable>MAX_OPERSTATE</replaceable></optional></term>
 
-        <listitem><para>Takes an operational state. Please see
-        <citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+        <listitem><para>Takes a minimum operational state and an optional maximum operational state.
+        Please see <citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
         for possible operational states. If set, the specified value overrides
         <varname>RequiredForOnline=</varname> settings in <filename>.network</filename> files.
         But this does not override operational states specified in <option>--interface=</option> option.
index b3ba621ae6c416574b23f858581f12c576530719..94a7bfd5ef6f50c8fd20f5f329a34c881758c52a 100644 (file)
     <filename>systemd-networkd</filename> is restarted, netdev interfaces for
     which configuration was removed will not be dropped, and may need to be
     cleaned up manually.</para>
+
+    <para><command>systemd-networkd</command> may be introspected and controlled at runtime using
+    <citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+    </para>
   </refsect1>
 
   <refsect1><title>Configuration Files</title>
   <refsect1>
     <title>See Also</title>
     <para>
+      <citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <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-wait-online.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-networkd-generator.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index 9e6cc8bf1a3b25987720f4147d37a862f70c5451..b269b9917034381d033f13a184a88c8ba216ca80 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 "30">
-<!ENTITY fedora_cloud_release "1.2">
+<!ENTITY fedora_latest_version "31">
+<!ENTITY fedora_cloud_release "1.9">
 ]>
 <!-- SPDX-License-Identifier: LGPL-2.1+ -->
 
           a server data partition which are mounted to the appropriate
           places in the container. All these partitions must be
           identified by the partition types defined by the <ulink
-          url="https://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/">Discoverable
+          url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable
           Partitions Specification</ulink>.</para></listitem>
 
           <listitem><para>No partition table, and a single file system spanning the whole image.</para></listitem>
 
       <programlisting># dnf -y --releasever=&fedora_latest_version; --installroot=/var/lib/machines/f&fedora_latest_version; \
       --disablerepo='*' --enablerepo=fedora --enablerepo=updates install \
-      systemd passwd dnf fedora-release vim-minimal
+      systemd passwd dnf fedora-release vim-minimal glibc-minimal-langpack
 # systemd-nspawn -bD /var/lib/machines/f&fedora_latest_version;</programlisting>
 
       <para>This installs a minimal Fedora distribution into the
similarity index 89%
rename from man/systemd-pstore.xml
rename to man/systemd-pstore.service.xml
index 11d66b8b012b8072c8a5073b8c5932d060167c10..47916da5219d6e658eb55b5d135dcf36d78249e3 100644 (file)
@@ -7,19 +7,19 @@
           xmlns:xi="http://www.w3.org/2001/XInclude">
 
   <refentryinfo>
-    <title>systemd-pstore</title>
+    <title>systemd-pstore.service</title>
     <productname>systemd</productname>
   </refentryinfo>
 
   <refmeta>
-    <refentrytitle>systemd-pstore</refentrytitle>
+    <refentrytitle>systemd-pstore.service</refentrytitle>
     <manvolnum>8</manvolnum>
   </refmeta>
 
   <refnamediv>
-    <refname>systemd-pstore</refname>
     <refname>systemd-pstore.service</refname>
-    <refpurpose>Tool to archive contents of the persistent storage filesystem</refpurpose>
+    <refname>systemd-pstore</refname>
+    <refpurpose>A service to archive contents of pstore</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
@@ -49,8 +49,8 @@
 
     <para>The pstore service is independent of the kdump service. In cloud environments
     specifically, host and guest filesystems are on remote filesystems (eg. iSCSI
-    or NFS), thus kdump relies [implicitly and/or explicitly] upon proper operation
-    of networking software *and* hardware *and* infrastructure.  Thus it may not be
+    or NFS), thus kdump relies (implicitly and/or explicitly) upon proper operation
+    of networking software *and* hardware *and* infrastructure. Thus it may not be
     possible to capture a kernel coredump to a file since writes over the network
     may not be possible.</para>
 
@@ -59,9 +59,9 @@
     debugging.</para>
 
     <para>The <command>systemd-pstore</command> executable does the actual work. Upon starting,
-    the <filename>pstore.conf</filename> is read to obtain options, then the /sys/fs/pstore
+    the <filename>pstore.conf</filename> file is read and the <filename>/sys/fs/pstore</filename>
     directory contents are processed according to the options. Pstore files are written to the
-    journal, and optionally saved into /var/lib/systemd/pstore.</para>
+    journal, and optionally saved into <filename>/var/lib/systemd/pstore</filename>.</para>
   </refsect1>
 
   <refsect1>
diff --git a/man/systemd-repart.xml b/man/systemd-repart.xml
new file mode 100644 (file)
index 0000000..f55be4f
--- /dev/null
@@ -0,0 +1,269 @@
+<?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-repart" conditional='ENABLE_REPART'
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>systemd-repart</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-repart</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-repart</refname>
+    <refname>systemd-repart.service</refname>
+    <refpurpose>Automatically grow and add partitions</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>systemd-repart</command>
+      <arg choice="opt" rep="repeat">OPTIONS</arg>
+      <arg choice="opt" rep="repeat"><replaceable><optional>BLOCKDEVICE</optional></replaceable></arg>
+    </cmdsynopsis>
+
+    <para><filename>systemd-repart.service</filename></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>systemd-repart</command> grows and adds partitions to a partition table, based on the
+    configuration files described in
+    <citerefentry><refentrytitle>repart.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+    </para>
+
+    <para>If invoked with no arguments, it operates on the block device backing the root file system partition
+    of the OS, thus growing and adding partitions of the booted OS image itself. When called in the initial
+    RAM disk it operates on the block device backing <filename>/sysroot/</filename> instead, i.e. on the
+    block device the system will soon transition into. The <filename>systemd-repart.service</filename>
+    service is generally run at boot in the initial RAM disk, in order to augment the partition table of the
+    OS before its partitions are mounted. <command>systemd-repart</command> (mostly) operates in a purely
+    incremental mode: it only grows existing and adds new partitions; it does not shrink, delete or move
+    existing partitions. The service is intended to be run on every boot, but when it detects that the
+    partition table already matches the installed <filename>repart.d/*.conf</filename> configuration
+    files, it executes no operation.</para>
+
+    <para><command>systemd-repart</command> is intended to be used when deploying OS images, to automatically
+    adjust them to the system they are running on, during first boot. This way the deployed image can be
+    minimal in size and may be augmented automatically at boot when needed, taking possession of disk space
+    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
+      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>
+    </itemizedlist>
+
+    <para>The algorithm executed by <command>systemd-repart</command> is roughly as follows:</para>
+
+    <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>
+
+      <listitem><para>The partition table already existing on the block device is loaded and
+      parsed.</para></listitem>
+
+      <listitem><para>The existing partitions in the partition table are matched up with the
+      <filename>repart.d/*.conf</filename> files by GPT partition type UUID. The first existing partition
+      of a specific type is assigned the first configuration file declaring the same type. The second
+      existing partition of a specific type is then assigned the second configuration file declaring the same
+      type, and so on. After this iterative assigning is complete any left-over existing partitions that have
+      no matching configuration file are considered "foreign" and left as they are. And any configuration
+      files for which no partition currently exists are understood as a request to create such a
+      partition.</para></listitem>
+
+      <listitem><para>Taking the size constraints and weights declared in the configuration files into
+      account, all partitions that shall be created are now allocated to the disk, taking up all free space,
+      always respecting the size and padding requests. Similar, existing partitions that are determined to
+      grow are grown. New partitions are always appended to the end of the existing partition table, taking
+      the first partition table slot whose index is greater than the indexes of all existing
+      partitions. Partition table slots are never reordered and thus partition numbers are ensured to remain
+      stable. Note that this allocation happens in RAM only, the partition table on disk is not updated
+      yet.</para></listitem>
+
+      <listitem><para>All existing partitions for which configuration files exist and which currently have no
+      GPT partition label set will be assigned a label, either explicitly configured in the configuration or
+      (if that's missing) derived automatically from the partition type. The same is done for all partitions
+      that are newly created. These assignments are done in RAM only, too, the disk is not updated
+      yet.</para></listitem>
+
+      <listitem><para>Similarly, all existing partitions for which configuration files exist and which
+      currently have an all-zero identifying UUID will be assigned a new UUID. This UUID is cryptographically
+      hashed from a common seed value together with the partition type UUID (and a counter in case multiple
+      partitions of the same type are defined), see below. The same is done for all partitions that are
+      created anew. These assignments are done in RAM only, too, the disk is not updated
+      yet.</para></listitem>
+
+      <listitem><para>Similarly, if the disk's volume UUID is all zeroes it is also initialized, also
+      cryptographically hashed from the same common seed value. Also, in RAM only, too.</para></listitem>
+
+      <listitem><para>The disk space assigned to new partitions (i.e. what was previously considered free
+      space but is no longer) is now erased. Specifically, all file system signatures are removed, and if the
+      device supports it the <constant>BLKDISCARD</constant> I/O control command is issued to inform the
+      hardware that the space is empty now. In addition any "padding" between partitions and at the end of
+      the device is similarly erased.</para></listitem>
+
+      <listitem><para>The new partition table is finally written to disk. The kernel is asked to reread the
+      partition table.</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 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
+    <varname>FactoryReset=</varname> boolean is deleted, and the algorithm restarted, thus immediately
+    re-creating these partitions anew empty.</para>
+
+    <para>Note that <command>systemd-repart</command> only changes partition tables, it does not create or
+    resize any file systems within these partitions. A separate mechanism should be used for that, for
+    example
+    <citerefentry><refentrytitle>systemd-growfs</refentrytitle><manvolnum>8</manvolnum></citerefentry> and
+    <command>systemd-makefs</command>.</para>
+
+    <para>The UUIDs identifying the new partitions created (or assigned to existing partitions that have no
+    UUID yet), as well as the disk as a whole are hashed cryptographically from a common seed value. This
+    seed value is usually the
+    <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry> of the
+    system, so that the machine ID reproducibly determines the UUIDs assigned to all partitions. If the
+    machine ID cannot be read (or the user passes <option>--seed=random</option>, see below) the seed is
+    generated randomly instead, so that the partition UUIDs are also effectively random. The seed value may
+    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>
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+
+    <para>The following options are understood:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><option>--dry-run=</option></term>
+        <listitem><para>Takes a boolean. If this switch is not specified <option>--dry-run=yes</option> is
+        the implied default. Controls whether <filename>systemd-repart</filename> executes the requested
+        re-partition operations or whether it should only show what it would do. Unless
+        <option>--dry-run=no</option> is specified <filename>systemd-repart</filename> will not actually
+        touch the device's partition table.</para></listitem>
+      </varlistentry>
+
+      <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>
+
+        <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
+        <literal>allow</literal> the command will extend an existing partition table or create a new one if
+        none exists. If <literal>require</literal> the command will create a new partition table if none
+        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>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--discard=</option></term>
+
+        <listitem><para>Takes a boolean. If this switch is not specified <option>--discard=yes</option> is
+        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>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--factory-reset=</option></term>
+
+        <listitem><para>Takes boolean. If this switch is not specified <option>--factory=reset=no</option> is
+        the implied default. Controls whether to operate in "factory reset" mode, see above. If set to true
+        this will remove all existing partitions marked with <varname>FactoryReset=</varname> set to yes
+        early while executing the re-partitioning algorithm. Use with care, this is a great way to lose all
+        your data. Note that partition files need to explicitly turn <varname>FactoryReset=</varname> on, as
+        the option defaults to off. If no partitions are marked for factory reset this switch has no
+        effect. Note that there are two other methods to request factory reset operation: via the kernel
+        command line and via an EFI variable, see above.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--can-factory-reset</option></term>
+
+        <listitem><para>If this switch is specified the disk is not re-partitioned. Instead it is determined
+        if any existing partitions are marked with <varname>FactoryReset=</varname>. If there are the tool
+        will exit with exit status zero, otherwise non-zero. This switch may be used to quickly determine
+        whether the running system supports a factory reset mechanism built on
+        <command>systemd-repart</command>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--root=</option></term>
+
+        <listitem><para>Takes a path to a directory to use as root file system when searching for
+        <filename>repart.d/*.conf</filename> files and for the machine ID file to use as seed. By default
+        when invoked on the regular system this defaults to the host's root file system
+        <filename>/</filename>. If invoked from the initial RAM disk this defaults to
+        <filename>/sysroot/</filename>, so that the tool operates on the configuration and machine ID stored
+        in the root file system later transitioned into itself.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--seed=</option></term>
+
+        <listitem><para>Takes a UUID as argument or the special value <constant>random</constant>. If a UUID
+        is specified the UUIDs to assign to partitions and the partition table itself are derived via
+        cryptographic hashing from it. If not specified it is attempted to read the machine ID from the host
+        (or more precisely, the root directory configured via <option>--root=</option>) and use it as seed
+        instead, falling back to a randomized seed otherwise. Use <option>--seed=random</option> to force a
+        randomized seed. Explicitly specifying the seed may be used to generated strictly reproducible
+        partition tables.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--pretty=</option></term>
+
+        <listitem><para>Takes a boolean argument. If this switch is not specified, it defaults to on when
+        called from an interactive terminal and off otherwise. Controls whether to show a user friendly table
+        and graphic illustrating the changes applied.</para></listitem>
+      </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>,
+        <filename>/run/repart.d/*.conf</filename>.</para></listitem>
+      </varlistentry>
+
+      <xi:include href="standard-options.xml" xpointer="help" />
+      <xi:include href="standard-options.xml" xpointer="version" />
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>repart.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index e403fa53086f5a5c3947554261380cabaa307145..e22b335d30d726affd00a25f6c18985c34358fdf 100644 (file)
         <term><varname>DefaultLimitRTPRIO=</varname></term>
         <term><varname>DefaultLimitRTTIME=</varname></term>
 
-        <listitem><para>These settings control various default
-        resource limits for units. See
-        <citerefentry><refentrytitle>setrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry>
-        for details. The resource limit is possible to specify in two formats,
-        <option>value</option> to set soft and hard limits to the same value,
-        or <option>soft:hard</option> to set both limits individually (e.g. DefaultLimitAS=4G:16G).
-        Use the string <varname>infinity</varname> to
-        configure no limit on a specific resource. The multiplicative
-        suffixes K (=1024), M (=1024*1024) and so on for G, T, P and E
-        may be used for resource limits measured in bytes
-        (e.g. DefaultLimitAS=16G). For the limits referring to time values,
-        the usual time units ms, s, min, h and so on may be used (see
-        <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>
-        for details). Note that if no time unit is specified for
-        <varname>DefaultLimitCPU=</varname> the default unit of seconds is
-        implied, while for <varname>DefaultLimitRTTIME=</varname> the default
-        unit of microseconds is implied. Also, note that the effective
-        granularity of the limits might influence their
-        enforcement. For example, time limits specified for
-        <varname>DefaultLimitCPU=</varname> will be rounded up implicitly to
-        multiples of 1s. These  settings may be overridden in individual units
-        using the corresponding LimitXXX= directives. Note that these resource
-        limits are only defaults for units, they are not applied to PID 1
-        itself.</para></listitem>
+        <listitem><para>These settings control various default resource limits for processes executed by
+        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
+        for units, they are not applied to the service manager process (i.e. PID 1) itself.</para></listitem>
       </varlistentry>
 
       <varlistentry>
diff --git a/man/systemd-userdbd.service.xml b/man/systemd-userdbd.service.xml
new file mode 100644 (file)
index 0000000..9d5841c
--- /dev/null
@@ -0,0 +1,69 @@
+<?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-userdbd.service" conditional='ENABLE_USERDB'>
+
+  <refentryinfo>
+    <title>systemd-userdbd.service</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-userdbd.service</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-userdbd.service</refname>
+    <refname>systemd-userdbd</refname>
+    <refpurpose>JSON User/Group Record Query Multiplexer/NSS Compatibility</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <para><filename>systemd-userdbd.service</filename></para>
+    <para><filename>/usr/lib/systemd/systemd-userdbd</filename></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>systemd-userdbd</command> is a system service that multiplexes user/group lookups to all
+    local services that provide JSON user/group record definitions to the system. In addition it synthesizes
+    JSON user/group records from classic UNIX/glibc NSS user/group records in order to provide full backwards
+    compatibility.</para>
+
+    <para>Most of <command>systemd-userdbd</command>'s functionality is accessible through the
+    <citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    command.</para>
+
+    <para>The user and group records this service provides access to follow the <ulink
+    url="https://systemd.io/USER_RECORD">JSON User Record</ulink> and <ulink
+    url="https://systemd.io/GROUP_RECORD">JSON Group Record</ulink> definitions. This service implements the
+    <ulink url="https://systemd.io/USER_GROUP_API">User/Group Record Lookup API via Varlink</ulink>, and
+    multiplexes access other services implementing this API, too. It is thus both server and client of this
+    API.</para>
+
+    <para>This service provides two distinct <ulink url="https://varlink.org/">Varlink</ulink> services:
+    <constant>io.systemd.Multiplexer</constant> provides a single, unified API for querying JSON user and
+    group records. Internally it talks to all other user/group record services running on the system in
+    parallel and forwards any information discovered. This simplifies clients substantially since they need
+    to talk to a single service only instead of all of them in
+    parallel. <constant>io.systemd.NameSeviceSwitch</constant> provides compatibility with classic UNIX/glibc
+    NSS user records, i.e. converts <type>struct passwd</type> and <type>struct group</type> records as
+    acquired with APIs such as <citerefentry
+    project='man-pages'><refentrytitle>getpwnam</refentrytitle><manvolnum>1</manvolnum></citerefentry> to JSON
+    user/group records, thus hiding the differences between the services as much as possible.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>nss-systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+</refentry>
index ee2ca6aa10f7497fb08e5504ddc62b180b47b4af..8f1695ad293f6113a4b32f58c18822687c4f8928 100644 (file)
       <option>syslog</option> or <option>kmsg</option> (or their combinations with console output, see below)
       automatically acquire dependencies of type <varname>After=</varname> on
       <filename>systemd-journald.socket</filename>.</para></listitem>
+
+      <listitem><para>Units using <varname>LogNamespace=</varname> will automatically gain ordering and
+      requirement dependencies on the two socket units associated with
+      <filename>systemd-journald@.service</filename> instances.</para></listitem>
     </itemizedlist>
   </refsect1>
 
       <varlistentry>
         <term><varname>RootImage=</varname></term>
 
-        <listitem><para>Takes a path to a block device node or regular file as argument. This call is similar to
-        <varname>RootDirectory=</varname> however mounts a file system hierarchy from a block device node or loopback
-        file instead of a directory. The device node or file system image file needs to contain a file system without a
-        partition table, or a file system within an MBR/MS-DOS or GPT partition table with only a single
-        Linux-compatible partition, or a set of file systems within a GPT partition table that follows the <ulink
-        url="https://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/">Discoverable Partitions
+        <listitem><para>Takes a path to a block device node or regular file as argument. This call is similar
+        to <varname>RootDirectory=</varname> however mounts a file system hierarchy from a block device node
+        or loopback file instead of a directory. The device node or file system image file needs to contain a
+        file system without a partition table, or a file system within an MBR/MS-DOS or GPT partition table
+        with only a single Linux-compatible partition, or a set of file systems within a GPT partition table
+        that follows the <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
         Specification</ulink>.</para>
 
         <para>When <varname>DevicePolicy=</varname> is set to <literal>closed</literal> or
         Linux systems.</para>
 
         <para>When used in conjunction with <varname>DynamicUser=</varname> the user/group name specified is
-        dynamically allocated at the time the service is started, and released at the time the service is stopped —
-        unless it is already allocated statically (see below). If <varname>DynamicUser=</varname> is not used the
-        specified user and group must have been created statically in the user database no later than the moment the
-        service is started, for example using the
-        <citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry> facility, which
-        is applied at boot or package install time.</para>
+        dynamically allocated at the time the service is started, and released at the time the service is
+        stopped — unless it is already allocated statically (see below). If <varname>DynamicUser=</varname>
+        is not used the specified user and group must have been created statically in the user database no
+        later than the moment the service is started, for example using the
+        <citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        facility, which is applied at boot or package install time. If the user does not exist by then
+        program invocation will fail.</para>
 
         <para>If the <varname>User=</varname> setting is used the supplementary group list is initialized
         from the specified user's default group list, as defined in the system's user and group
@@ -404,11 +409,11 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
         <varname>RestrictAddressFamilies=</varname>, <varname>RestrictNamespaces=</varname>,
         <varname>PrivateDevices=</varname>, <varname>ProtectKernelTunables=</varname>,
         <varname>ProtectKernelModules=</varname>, <varname>ProtectKernelLogs=</varname>,
-        <varname>MemoryDenyWriteExecute=</varname>, <varname>RestrictRealtime=</varname>,
-        <varname>RestrictSUIDSGID=</varname>, <varname>DynamicUser=</varname> or <varname>LockPersonality=</varname>
-        are specified. Note that even if this setting is overridden by them, <command>systemctl show</command> shows the
-        original value of this setting. Also see <ulink
-        url="https://www.kernel.org/doc/html/latest/userspace-api/no_new_privs.html">No New Privileges
+        <varname>ProtectClock=</varname>, <varname>MemoryDenyWriteExecute=</varname>,
+        <varname>RestrictRealtime=</varname>, <varname>RestrictSUIDSGID=</varname>, <varname>DynamicUser=</varname>
+        or <varname>LockPersonality=</varname> are specified. Note that even if this setting is overridden by them,
+        <command>systemctl show</command> shows the original value of this setting.
+        Also see <ulink url="https://www.kernel.org/doc/html/latest/userspace-api/no_new_privs.html">No New Privileges
         Flag</ulink>.</para></listitem>
       </varlistentry>
 
@@ -497,42 +502,51 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
         <term><varname>LimitRTTIME=</varname></term>
 
         <listitem><para>Set soft and hard limits on various resources for executed processes. See
-        <citerefentry><refentrytitle>setrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry> for details on
-        the resource limit concept. Resource limits may be specified in two formats: either as single value to set a
-        specific soft and hard limit to the same value, or as colon-separated pair <option>soft:hard</option> to set
-        both limits individually (e.g. <literal>LimitAS=4G:16G</literal>).  Use the string <option>infinity</option> to
-        configure no limit on a specific resource. The multiplicative suffixes K, M, G, T, P and E (to the base 1024)
-        may be used for resource limits measured in bytes (e.g. LimitAS=16G). For the limits referring to time values,
-        the usual time units ms, s, min, h and so on may be used (see
+        <citerefentry><refentrytitle>setrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
+        details on the resource limit concept. Resource limits may be specified in two formats: either as
+        single value to set a specific soft and hard limit to the same value, or as colon-separated pair
+        <option>soft:hard</option> to set both limits individually (e.g. <literal>LimitAS=4G:16G</literal>).
+        Use the string <option>infinity</option> to configure no limit on a specific resource. The
+        multiplicative suffixes K, M, G, T, P and E (to the base 1024) may be used for resource limits
+        measured in bytes (e.g. <literal>LimitAS=16G</literal>). For the limits referring to time values, the
+        usual time units ms, s, min, h and so on may be used (see
         <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
-        details). Note that if no time unit is specified for <varname>LimitCPU=</varname> the default unit of seconds
-        is implied, while for <varname>LimitRTTIME=</varname> the default unit of microseconds is implied. Also, note
-        that the effective granularity of the limits might influence their enforcement. For example, time limits
-        specified for <varname>LimitCPU=</varname> will be rounded up implicitly to multiples of 1s. For
-        <varname>LimitNICE=</varname> the value may be specified in two syntaxes: if prefixed with <literal>+</literal>
-        or <literal>-</literal>, the value is understood as regular Linux nice value in the range -20..19. If not
-        prefixed like this the value is understood as raw resource limit parameter in the range 0..40 (with 0 being
-        equivalent to 1).</para>
-
-        <para>Note that most process resource limits configured with these options are per-process, and processes may
-        fork in order to acquire a new set of resources that are accounted independently of the original process, and
-        may thus escape limits set. Also note that <varname>LimitRSS=</varname> is not implemented on Linux, and
-        setting it has no effect. Often it is advisable to prefer the resource controls listed in
+        details). Note that if no time unit is specified for <varname>LimitCPU=</varname> the default unit of
+        seconds is implied, while for <varname>LimitRTTIME=</varname> the default unit of microseconds is
+        implied. Also, note that the effective granularity of the limits might influence their
+        enforcement. For example, time limits specified for <varname>LimitCPU=</varname> will be rounded up
+        implicitly to multiples of 1s. For <varname>LimitNICE=</varname> the value may be specified in two
+        syntaxes: if prefixed with <literal>+</literal> or <literal>-</literal>, the value is understood as
+        regular Linux nice value in the range -20..19. If not prefixed like this the value is understood as
+        raw resource limit parameter in the range 0..40 (with 0 being equivalent to 1).</para>
+
+        <para>Note that most process resource limits configured with these options are per-process, and
+        processes may fork in order to acquire a new set of resources that are accounted independently of the
+        original process, and may thus escape limits set. Also note that <varname>LimitRSS=</varname> is not
+        implemented on Linux, and setting it has no effect. Often it is advisable to prefer the resource
+        controls listed in
         <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-        over these per-process limits, as they apply to services as a whole, may be altered dynamically at runtime, and
-        are generally more expressive. For example, <varname>MemoryLimit=</varname> is a more powerful (and working)
-        replacement for <varname>LimitRSS=</varname>.</para>
-
-        <para>For system units these resource limits may be chosen freely. For user units however (i.e. units run by a
-        per-user instance of
-        <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>), these limits are
-        bound by (possibly more restrictive) per-user limits enforced by the OS.</para>
+        over these per-process limits, as they apply to services as a whole, may be altered dynamically at
+        runtime, and are generally more expressive. For example, <varname>MemoryMax=</varname> is a more
+        powerful (and working) replacement for <varname>LimitRSS=</varname>.</para>
 
         <para>Resource limits not configured explicitly for a unit default to the value configured in the various
         <varname>DefaultLimitCPU=</varname>, <varname>DefaultLimitFSIZE=</varname>, … options available in
         <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>, and –
         if not configured there – the kernel or per-user defaults, as defined by the OS (the latter only for user
-        services, see above).</para>
+        services, see below).</para>
+
+        <para>For system units these resource limits may be chosen freely. When these settings are configured
+        in a user service (i.e. a service run by the per-user instance of the service manager) they cannot be
+        used to raise the limits above those set for the user manager itself when it was first invoked, as
+        the user's service manager generally lacks the privileges to do so. In user context these
+        configuration options are hence only useful to lower the limits passed in or to raise the soft limit
+        to the maximum of the hard limit as configured for the user. To raise the user's limits further, the
+        available configuration mechanisms differ between operating systems, but typically require
+        privileges. In most cases it is possible to configure higher per-user resource limits via PAM or by
+        setting limits on the system service encapsulating the user's service manager, i.e. the user's
+        instance of <filename>user@.service</filename>. After making such changes, make sure to restart the
+        user's service manager.</para>
 
         <table>
           <title>Resource limit directives, their equivalent <command>ulimit</command> shell commands and the unit used</title>
@@ -1286,6 +1300,21 @@ BindReadOnlyPaths=/var/lib/systemd</programlisting>
         <xi:include href="system-only.xml" xpointer="singular"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>ProtectClock=</varname></term>
+
+        <listitem><para>Takes a boolean argument. If set, writes to the hardware clock or system clock will be denied.
+        It is recommended to turn this on for most services that do not need modify the clock. Defaults to off. Enabling
+        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
+        <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        for the details about <varname>DeviceAllow=</varname>.</para>
+
+        <xi:include href="system-only.xml" xpointer="singular"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>ProtectKernelTunables=</varname></term>
 
@@ -1797,7 +1826,7 @@ SystemCallErrorNumber=EPERM</programlisting>
         mappings. Specifically these are the options <varname>PrivateTmp=</varname>,
         <varname>PrivateDevices=</varname>, <varname>ProtectSystem=</varname>, <varname>ProtectHome=</varname>,
         <varname>ProtectKernelTunables=</varname>, <varname>ProtectControlGroups=</varname>,
-        <varname>ProtectKernelLogs=</varname>, <varname>ReadOnlyPaths=</varname>,
+        <varname>ProtectKernelLogs=</varname>, <varname>ProtectClock=</varname>, <varname>ReadOnlyPaths=</varname>,
         <varname>InaccessiblePaths=</varname> and <varname>ReadWritePaths=</varname>.</para></listitem>
       </varlistentry>
 
@@ -2229,6 +2258,36 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
         </para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>LogNamespace=</varname></term>
+
+        <listitem><para>Run the unit's processes in the specified journal namespace. Expects a short
+        user-defined string identifying the namespace. If not used the processes of the service are run in
+        the default journal namespace, i.e. their log stream is collected and processed by
+        <filename>systemd-journald.service</filename>. If this option is used any log data generated by
+        processes of this unit (regardless if via the <function>syslog()</function>, journal native logging
+        or stdout/stderr logging) is collected and processed by an instance of the
+        <filename>systemd-journald@.service</filename> template unit, which manages the specified
+        namespace. The log data is stored in a data store independent from the default log namespace's data
+        store. See
+        <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        for details about journal namespaces.</para>
+
+        <para>Internally, journal namespaces are implemented through Linux mount namespacing and
+        over-mounting the directory that contains the relevant <constant>AF_UNIX</constant> sockets used for
+        logging in the unit's mount namespace. Since mount namespaces are used this setting disconnects
+        propagation of mounts from the unit's processes to the host, similar to how
+        <varname>ReadOnlyPaths=</varname> and similar settings (see above) work. Journal namespaces may hence
+        not be used for services that need to establish mount points on the host.</para>
+
+        <para>When this option is used the unit will automatically gain ordering and requirement dependencies
+        on the two socket units associated with the <filename>systemd-journald@.service</filename> instance
+        so that they are automatically established prior to the unit starting up. Note that when this option
+        is used log output of this service does not appear in the regular
+        <citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+        output, unless the <option>--namespace=</option> option is used.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>SyslogIdentifier=</varname></term>
 
index 9248726b9ec70a5b50dc2797ebe2c32d574e8d77..a0771f3c135a2504fdec1a2e9d5f4311396aaefd 100644 (file)
           marking the log line end.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>_NAMESPACE=</varname></term>
+
+        <listitem><para>If this file was written by a <command>systemd-journald</command> instance managing a
+        journal namespace that is not the default, this field contains the namespace identifier. See
+        <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        for details about journal namespaces.</para>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index 9104dd7f8f45289ca627d676c46ab89c0485a799..e04618340bd2cdcb3dbd2418e9be3927ecdf8593 100644 (file)
   <refsect1>
     <title>Description</title>
 
-    <para>Network link configuration is performed by the
-    <command>net_setup_link</command> udev builtin.</para>
+    <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
+    particular its <command>net_setup_link</command> builtin. See
+    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry> for a
+    general description of the syntax.</para>
 
     <para>The link files are read from the files located in the system
     network directory <filename>/usr/lib/systemd/network</filename>,
       <varlistentry id='type'>
         <term><varname>Type=</varname></term>
         <listitem>
-            <para>A whitespace-separated list of shell-style globs matching the device type, as exposed by
-            the udev property <literal>DEVTYPE</literal>. If the list is prefixed with a "!", the test is
-            inverted.</para>
+          <para>A whitespace-separated list of shell-style globs matching the device type, as exposed by
+          <command>networkctl status</command>. If the list is prefixed with a "!", the test is inverted.
+          </para>
         </listitem>
       </varlistentry>
 
           </para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>ReceiveChecksumOffload=</varname></term>
+        <listitem>
+          <para>Takes a boolean. If set to true, the hardware offload for checksumming of ingress
+          network packets is enabled. When unset, the kernel's default will be used.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>TransmitChecksumOffload=</varname></term>
+        <listitem>
+          <para>Takes a boolean. If set to true, the hardware offload for checksumming of egress
+          network packets is enabled. When unset, the kernel's default will be used.</para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><varname>TCPSegmentationOffload=</varname></term>
         <listitem>
           When unset, the kernel's default will be used.</para>
         </listitem>
       </varlistentry>
-    <varlistentry>
+      <varlistentry>
         <term><varname>GenericReceiveOffload=</varname></term>
         <listitem>
           <para>Takes a boolean. If set to true, the Generic Receive Offload (GRO) is enabled.
           <para>Takes a integer. Specifies the NIC transmit ring buffer size. 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 fe8a6f00cee1ec0bc7ddb2f802b8fec80f110804..d775d74053fdf14180a05453f3d8381ce8e6c7b8 100644 (file)
         <term><option>x-systemd.before=</option></term>
         <term><option>x-systemd.after=</option></term>
 
-        <listitem><para>Configures a <varname>Before=</varname>
-        dependency or <varname>After=</varname> between the created
-        mount unit and another systemd unit, such as a mount unit.
+        <listitem><para>In the created mount unit, configures a
+        <varname>Before=</varname> or <varname>After=</varname>
+        dependency on another systemd unit, such as a mount unit.
         The argument should be a unit name or an absolute path
         to a mount point. This option may be specified more than once.
         This option is particularly useful for mount point declarations
         for details.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>x-systemd.wanted-by=</option></term>
+        <term><option>x-systemd.required-by=</option></term>
+
+        <listitem><para>In the created mount unit, configures a
+        <varname>WantedBy=</varname> or <varname>RequiredBy=</varname>
+        dependency on another unit.  This option may be
+        specified more than once. If this is specified, the normal
+        automatic dependencies on the created mount unit, e.g.,
+        <filename>local-fs.target</filename>, are not automatically
+        created. See <varname>WantedBy=</varname> and <varname>RequiredBy=</varname> in
+        <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        for details.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>x-systemd.requires-mounts-for=</option></term>
 
index 70b4b27d7b3a6d70e579a83e0bfb61a592a136d6..4fc85d40ec3892468683d998fc33699f5a13b00f 100644 (file)
@@ -79,7 +79,7 @@
           </row>
           <row>
             <entry><constant>sl</constant></entry>
-            <entry>serial line IP (slip)</entry>
+            <entry>Serial line IP (slip)</entry>
           </row>
           <row>
             <entry><constant>wl</constant></entry>
index 6c4532b2b93cbdb27157f82404dab1db2f7d6c85..6ad1dc9e73714598fa1bdec079bbec7d5a9969e2 100644 (file)
   <refsect1>
     <title>Description</title>
 
-    <para>Network setup is performed by
+    <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>.
-    </para>
+    See <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    for a general description of the syntax.</para>
 
     <para>The main Virtual Network Device file must have the extension <filename>.netdev</filename>;
     other extensions are ignored. Virtual network devices are created as soon as networkd is
         </listitem>
       </varlistentry>
       <varlistentry>
-        <term><varname>DestinationPort=</varname></term>
+        <term><varname>UDPDestinationPort=</varname></term>
         <listitem>
           <para>Specifies destination port. When UDP encapsulation is selected it's mandotory. Ignored when ip
           encapsulation is selected.</para>
@@ -2112,7 +2113,7 @@ Endpoint=wireguard.example.com:51820</programlisting>
 
     <example>
       <title>/etc/systemd/network/27-xfrm.netdev</title>
-      <programlisting>[Xfrm]
+      <programlisting>[NetDev]
 Name=xfrm0
 Kind=xfrm
 
index 8a75cfc1cde2ee1a669376e17dd894d89176649b..da48dd04f4b2ff86f6c99ead085fc61226412378 100644 (file)
   <refsect1>
     <title>Description</title>
 
-    <para>Network setup is performed by
+    <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>.
-    </para>
+    See <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    for a general description of the syntax.</para>
 
     <para>The main network file must have the extension <filename>.network</filename>; other
     extensions are ignored. Networks are applied to links whenever the links appear.</para>
       <varlistentry>
         <term><varname>RequiredForOnline=</varname></term>
         <listitem>
-          <para>Takes a boolean or operational state. Please see
-          <citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+          <para>Takes a boolean or a minimum operational state and an optional maximum operational state.
+          Please see <citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
           for possible operational states. When <literal>yes</literal>, the network is deemed required when
           determining whether the system is online when running
           <command>systemd-networkd-wait-online</command>. When <literal>no</literal>, the network is ignored
-          when checking for online state. When an operational state is set, <literal>yes</literal> is implied,
-          and this controls the operational state required for the network interface to be considered online.
+          when checking for online state. When a minimum operational state and an optional maximum operational
+          state are set, <literal>yes</literal> is implied, and this controls the minimum and maximum
+          operational state required for the network interface to be considered online.
           Defaults to <literal>yes</literal>.</para>
 
           <para>The network will be brought up normally in all cases, but in
         <varlistentry>
           <term><varname>IPv6Token=</varname></term>
           <listitem>
-            <para>An IPv6 address with the top 64 bits unset. When set, indicates the
-            64-bit interface part of SLAAC IPv6 addresses for this link. Note that
-            the token is only ever used for SLAAC, and not for DHCPv6 addresses, even
-            in the case DHCP is requested by router advertisement. By default, the
-            token is autogenerated.</para>
+            <para>Specifies an optional address generation mode and a required IPv6 address. If
+            the mode is present, the two parts must be separated with a colon
+            <literal><replaceable>mode</replaceable>:<replaceable>address</replaceable></literal>. The
+            address generation mode may be either <constant>prefixstable</constant> or
+            <constant>static</constant>. If not specified, <constant>static</constant> is assumed.
+            </para>
+            <para>When the mode is set to <constant>static</constant>, or unspecified, the lower bits of
+            the supplied address are combined with the upper bits of a prefix received in a Router Advertisement
+            message to form a complete address. Note that if multiple prefixes are received in an RA message, or in
+            multiple RA messages, addresses will be formed from each of them using the supplied address. This
+            mode implements SLAAC but uses a static interface identifier instead of an identifier generated
+            using the EUI-64 algorithm. Because the interface identifier is static, if Duplicate Address Detection
+            detects that the computed address is a duplicate (in use by another node on the link), then this
+            mode will fail to provide an address for that prefix.
+            </para>
+            <para>When the mode is set to <literal>prefixstable</literal> the RFC 7217 algorithm for generating
+            interface identifiers will be used, but only when a prefix received in an RA message matches the supplied address.
+            See <ulink url="https://tools.ietf.org/html/rfc7217">RFC 7217</ulink>. Prefix matching will be attempted
+            against each <constant>prefixstable</constant> IPv6Token variable provided in the configuration; if a received
+            prefix does not match any of the provided addresses, then the EUI-64 algorithm will be used to form
+            an interface identifier for that prefix. This mode is also SLAAC, but with a potentially stable interface
+            identifier which does not directly map to the interface's hardware address.
+
+            Note that the <constant>prefixstable</constant> algorithm includes both the interface's name and
+            MAC address in the hash used to compute the interface identifier, so if either of those are changed the resulting
+            interface identifier (and address) will change, even if the prefix received in the RA message has not changed.
+
+            Note that if multiple <constant>prefixstable</constant> IPv6Token variables are supplied with addresses that
+            match a prefix received in an RA message, only the first one will be used to generate addresses.
+            </para>
           </listitem>
         </varlistentry>
         <varlistentry>
         <varlistentry>
           <term><varname>Domains=</varname></term>
           <listitem>
-            <para>A list of domains which should be resolved using the DNS servers on 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 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>A whitespace-separated list of domains which should be resolved using the DNS servers on
+            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
+            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
             ending in those domains (hence also single label names, if any "search domains" are listed), are routed to
         </varlistentry>
         <varlistentry>
           <term><varname>IPv6AcceptRA=</varname></term>
-          <listitem><para>Takes a boolean. Controls IPv6 Router Advertisement (RA) reception support
-          for the interface. If true, RAs are accepted; if false, RAs are ignored, independently of the
-          local forwarding state. When RAs are accepted, they may 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.</para>
+          <listitem><para>Takes a boolean. Controls IPv6 Router Advertisement (RA) reception support for the
+          interface. If true, RAs are accepted; if false, RAs are ignored. When RAs are accepted, they may
+          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>
 
           <para>Further settings for the IPv6 RA support may be configured in the
           <literal>[IPv6AcceptRA]</literal> section, see below.</para>
         <varlistentry>
           <term><varname>InvertRule=</varname></term>
           <listitem>
-            <para>A boolean. Specifies whether the rule to be inverted. Defaults to false.</para>
+            <para>A boolean. Specifies whether the rule is to be inverted. Defaults to false.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
             <literal>ipv4</literal>.</para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><varname>User=</varname></term>
+          <listitem>
+            <para>Takes a username, a user ID, or a range of user IDs separated by a dash. Defaults to
+            unset.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>SuppressPrefixLength=</varname></term>
+          <listitem>
+            <para>Takes a number <replaceable>N</replaceable> in the range 0-128 and rejects routing
+            decisions that have a prefix length of <replaceable>N</replaceable> or less. Defaults to
+            unset.</para>
+          </listitem>
+        </varlistentry>
       </variablelist>
     </refsect1>
 
         <varlistentry>
           <term><varname>Gateway=</varname></term>
           <listitem>
-            <para>Takes the gateway address or special value <literal>dhcp</literal>. If
-            <literal>dhcp</literal>, then the gateway address provided by DHCP (or in the IPv6 case,
+            <para>Takes the gateway address or special value <literal>_dhcp</literal>. If
+            <literal>_dhcp</literal>, then the gateway address provided by DHCP (or in the IPv6 case,
             provided by IPv6 RA) is used.</para>
           </listitem>
         </varlistentry>
             <para>Note that this configuration will overwrite others.
             In concrete, the following variables will be ignored:
             <varname>SendHostname=</varname>, <varname>ClientIdentifier=</varname>,
-            <varname>UseRoutes=</varname>, <varname>SendHostname=</varname>,
-            <varname>UseMTU=</varname>, <varname>VendorClassIdentifier=</varname>,
-            <varname>UseTimezone=</varname>.</para>
+            <varname>UseRoutes=</varname>, <varname>UseMTU=</varname>,
+            <varname>VendorClassIdentifier=</varname>, <varname>UseTimezone=</varname>.</para>
 
             <para>With this option enabled DHCP requests will mimic those generated by Microsoft Windows, in
             order to reduce the ability to fingerprint and recognize installations. This means DHCP request
             a prefix-hint in the DHCPv6 solicitation. Prefix ranges 1-128. Defaults to unset.</para>
           </listitem>
         </varlistentry>
+
+        <varlistentry>
+          <term><varname>WithoutRA=</varname></term>
+          <listitem>
+            <para>When true, DHCPv6 client starts without router advertisements's managed or other address configuration flag.
+            Defaults to false.</para>
+          </listitem>
+        </varlistentry>
       </variablelist>
   </refsect1>
 
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term><varname>DHCPv6Client=</varname></term>
+          <listitem>
+            <para>Takes a boolean. When true (the default), the DHCPv6 client will be started when the
+            RA has the managed or other information flag.</para>
+          </listitem>
+        </varlistentry>
+
       </variablelist>
   </refsect1>
 
         <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 distributed via Router Advertisement
-        messages when <varname>EmitDNS=</varname> is true. 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.
+        <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>
       </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>
 
             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>
   </refsect1>
 
   <refsect1>
-    <title>[TrafficControlQueueingDiscipline] Section Options</title>
-    <para>The <literal>[TrafficControlQueueingDiscipline]</literal> section manages the Traffic control. 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>
+    <title>[QDisc] Section Options</title>
+    <para>The <literal>[QDisc]</literal> section manages the traffic control queueing discipline (qdisc).</para>
+
+    <variablelist class='network-directives'>
+      <varlistentry>
+        <term><varname>Parent=</varname></term>
+        <listitem>
+          <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>clsact</literal>
+          or <literal>ingress</literal>. This is mandatory.</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>
+    </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>
 
     <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>
+          <literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
+          major and minor number in hexadecimal ranges 1 to ffff separated with a colon
+          (<literal>major:minor</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>
 
       <varlistentry>
-        <term><varname>NetworkEmulatorDelaySec=</varname></term>
+        <term><varname>DelaySec=</varname></term>
         <listitem>
           <para>Specifies the fixed amount of delay to be added to all packets going out of the
           interface. Defaults to unset.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>NetworkEmulatorDelayJitterSec=</varname></term>
+        <term><varname>DelayJitterSec=</varname></term>
         <listitem>
           <para>Specifies the chosen delay to be added to the packets outgoing to the network
           interface. Defaults to unset.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>NetworkEmulatorPacketLimit=</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>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>NetworkEmulatorLossRate=</varname></term>
+        <term><varname>LossRate=</varname></term>
         <listitem>
           <para>Specifies an independent loss probability to be added to the packets outgoing from the
           network interface. Takes a percentage value, suffixed with "%". Defaults to unset.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>NetworkEmulatorDuplicateRate=</varname></term>
+        <term><varname>DuplicateRate=</varname></term>
         <listitem>
           <para>Specifies that the chosen percent of packets is duplicated before queuing them.
           Takes a percentage value, suffixed with "%". Defaults to unset.</para>
         </listitem>
       </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[TokenBucketFilter] Section Options</title>
+    <para>The <literal>[TokenBucketFilter]</literal> 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>, <literal>ingress</literal> or a class id. The class id takes the
+          major and minor number in hexadecimal ranges 1 to ffff separated with a colon
+          (<literal>major:minor</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>
 
       <varlistentry>
-        <term><varname>TokenBufferFilterLatencySec=</varname></term>
+        <term><varname>LatencySec=</varname></term>
         <listitem>
           <para>Specifies the latency parameter, which specifies the maximum amount of time a
-          packet can sit in the Token Buffer Filter (TBF). Defaults to unset.</para>
+          packet can sit in the Token Bucket Filter (TBF). Defaults to unset.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>TokenBufferFilterLimitSize=</varname></term>
+        <term><varname>LimitSize=</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,
       </varlistentry>
 
       <varlistentry>
-        <term><varname>TokenBufferFilterBurst=</varname></term>
+        <term><varname>Burst=</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
       </varlistentry>
 
       <varlistentry>
-        <term><varname>TokenBufferFilterRate=</varname></term>
+        <term><varname>Rate=</varname></term>
         <listitem>
           <para>Specifies the device specific bandwidth. When suffixed with K, M, or G, the specified
           bandwidth is parsed as Kilobits, Megabits, or Gigabits, respectively, to the base of 1000.
       </varlistentry>
 
       <varlistentry>
-        <term><varname>TokenBufferFilterMPUBytes=</varname></term>
+        <term><varname>MPUBytes=</varname></term>
         <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,
       </varlistentry>
 
       <varlistentry>
-        <term><varname>TokenBufferFilterPeakRate=</varname></term>
+        <term><varname>PeakRate=</varname></term>
         <listitem>
           <para>Takes the maximum depletion rate of the bucket. When suffixed with K, M, or G, the
           specified size is parsed as Kilobits, Megabits, or Gigabits, respectively, to the base of
       </varlistentry>
 
       <varlistentry>
-        <term><varname>TokenBufferFilterMTUBytes=</varname></term>
+        <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.
           Defaults to unset.</para>
         </listitem>
       </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[StochasticFairBlue] Section Options</title>
+    <para>The <literal>[StochasticFairBlue]</literal> section manages the queueing discipline
+    (qdisc) of stochastic fair blue (sfb).</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>StochasticFairnessQueueingPerturbPeriodSec=</varname></term>
+        <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>
+
+      <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>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[StochasticFairnessQueueing] Section Options</title>
+    <para>The <literal>[StochasticFairnessQueueing]</literal> section manages the queueing discipline
+    (qdisc) of stochastic fairness queueing (sfq).</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>, <literal>ingress</literal> or a class id. The class id takes the
+          major and minor number in hexadecimal ranges 1 to ffff separated with a colon
+          (<literal>major:minor</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>
+
+      <varlistentry>
+        <term><varname>PerturbPeriodSec=</varname></term>
         <listitem>
           <para>Specifies the interval in seconds for queue algorithm perturbation. Defaults to unset.</para>
         </listitem>
       </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[PFIFO] Section Options</title>
+    <para>The <literal>[PFIFO]</literal> section manages the queueing discipline (qdisc) of
+    Packet First In First Out (pfifo).</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>
+
+      <varlistentry>
+        <term><varname>PacketLimit=</varname></term>
+        <listitem>
+          <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 ranges 0 to 4294967294. Defaults to unset and kernel's default is used.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[CAKE] Section Options</title>
+    <para>The <literal>[CAKE]</literal> section manages the queueing discipline (qdisc) of
+    Common Applications Kept Enhanced (CAKE).</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>, <literal>ingress</literal> or a class id. The class id takes the
+          major and minor number in hexadecimal ranges 1 to ffff separated with a colon
+          (<literal>major:minor</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>
+
+       <varlistentry>
+        <term><varname>Overhead=</varname></term>
+        <listitem>
+          <para>Specifies that bytes to be addeded to the size of each packet. Bytes may be negative.
+          Takes an integer ranges -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 <literal>[ControlledDelay]</literal> section manages the queueing discipline (qdisc) of
+    controlled delay (CoDel).</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>, <literal>ingress</literal> or a class id. The class id takes the
+          major and minor number in hexadecimal ranges 1 to ffff separated with a colon
+          (<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
+        </listitem>
+      </varlistentry>
 
       <varlistentry>
-        <term><varname>ControlledDelayPacketLimit=</varname></term>
+        <term><varname>Handle=</varname></term>
         <listitem>
-          <para>Specifies the hard lmit on the queue size in number of packets. When this limit is reached, incoming packets are
+          <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>
+
+      <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>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>ControlledDelayTargetSec=</varname></term>
+        <term><varname>TargetSec=</varname></term>
         <listitem>
           <para>Takes a timespan. Specifies the acceptable minimum standing/persistent queue delay.
           Defaults to unset and kernel's default is used.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>ControlledDelayIntervalSec=</varname></term>
+        <term><varname>IntervalSec=</varname></term>
         <listitem>
           <para>Takes a timespan. This is used to ensure that the measured minimum delay does not
           become too stale. Defaults to unset and kernel's default is used.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>ControlledDelayECN=</varname></term>
+        <term><varname>ECN=</varname></term>
         <listitem>
           <para>Takes a boolean. This can be used to mark packets instead of dropping them. Defaults to
           unset and kernel's default is used.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>ControlledDelayCEThresholdSec=</varname></term>
+        <term><varname>CEThresholdSec=</varname></term>
         <listitem>
           <para>Takes a timespan. This sets a threshold above which all packets are marked with ECN
           Congestion Experienced (CE). Defaults to unset and kernel's default is used.</para>
         </listitem>
       </varlistentry>
+    </variablelist>
+  </refsect1>
 
+  <refsect1>
+    <title>[GenericRandomEarlyDetection] Section Options</title>
+    <para>The <literal>[GenericRandomEarlyDetection]</literal> section manages the queueing discipline
+    (qdisc) of Generic Random Early Detection (GRED).</para>
+
+    <variablelist class='network-directives'>
       <varlistentry>
-        <term><varname>FairQueuingControlledDelayPacketLimit=</varname></term>
+        <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>
+
+      <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 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>GenericRIO=</varname></term>
+        <listitem>
+          <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 <literal>[FairQueueingControlledDelay]</literal> section manages the queueing discipline
+    (qdisc) of fair queuing controlled delay (FQ-CoDel).</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>, <literal>ingress</literal> or a class id. The class id takes the
+          major and minor number in hexadecimal ranges 1 to ffff separated with a colon
+          (<literal>major:minor</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>
+
+      <varlistentry>
+        <term><varname>PacketLimit=</varname></term>
         <listitem>
           <para>Specifies the hard limit on the real queue size. When this limit is reached, incoming packets are
           dropped. Defaults to unset and kernel's default is used.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>FairQueuingControlledDelayMemoryLimit=</varname></term>
+        <term><varname>MemoryLimit=</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>FairQueuingControlledDelayFlows=</varname></term>
+        <term><varname>Flows=</varname></term>
         <listitem>
           <para>Specifies the number of flows into which the incoming packets are classified.
           Defaults to unset and kernel's default is used.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>FairQueuingControlledDelayTargetSec=</varname></term>
+        <term><varname>TargetSec=</varname></term>
         <listitem>
           <para>Takes a timespan. Specifies the acceptable minimum standing/persistent queue delay.
           Defaults to unset and kernel's default is used.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>FairQueuingControlledDelayIntervalSec=</varname></term>
+        <term><varname>IntervalSec=</varname></term>
         <listitem>
           <para>Takes a timespan. This is used to ensure that the measured minimum delay does not
           become too stale. Defaults to unset and kernel's default is used.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>FairQueuingControlledDelayQuantum=</varname></term>
+        <term><varname>Quantum=</varname></term>
         <listitem>
           <para>Specifies the number of bytes used as 'deficit' in the fair queuing algorithmtimespan.
           When suffixed with K, M, or G, the specified size is parsed as Kilobytes, Megabytes, or Gigabytes,
       </varlistentry>
 
       <varlistentry>
-        <term><varname>FairQueuingControlledDelayECN=</varname></term>
+        <term><varname>ECN=</varname></term>
         <listitem>
           <para>Takes a boolean. This can be used to mark packets instead of dropping them. Defaults to
           unset and kernel's default is used.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>FairQueuingControlledDelayCEThresholdSec=</varname></term>
+        <term><varname>CEThresholdSec=</varname></term>
         <listitem>
           <para>Takes a timespan. This sets a threshold above which all packets are marked with ECN
           Congestion Experienced (CE). Defaults to unset and kernel's default is used.</para>
         </listitem>
       </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[FairQueueing] Section Options</title>
+    <para>The <literal>[FairQueueing]</literal> 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>, <literal>ingress</literal> or a class id. The class id takes the
+          major and minor number in hexadecimal ranges 1 to ffff separated with a colon
+          (<literal>major:minor</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>
 
       <varlistentry>
-        <term><varname>FairQueueTrafficPolicingPacketLimit=</varname></term>
+        <term><varname>PacketLimit=</varname></term>
         <listitem>
           <para>Specifies the hard limit on the real queue size. When this limit is reached, incoming packets are
           dropped. Defaults to unset and kernel's default is used.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>FairQueueTrafficPolicingFlowLimit=</varname></term>
+        <term><varname>FlowLimit=</varname></term>
         <listitem>
           <para>Specifies the hard limit on the maximum number of packets queued per flow. Defaults to
           unset and kernel's default is used.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>FairQueueTrafficPolicingQuantum=</varname></term>
+        <term><varname>Quantum=</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>FairQueueTrafficPolicingInitialQuantum=</varname></term>
+        <term><varname>InitialQuantum=</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
       </varlistentry>
 
       <varlistentry>
-        <term><varname>FairQueueTrafficPolicingMaximumRate=</varname></term>
+        <term><varname>MaximumRate=</varname></term>
         <listitem>
           <para>Specifies the maximum sending rate of a flow. When suffixed with K, M, or G, the
           specified size is parsed as Kilobits, Megabits, or Gigabits, respectively, to the base of
       </varlistentry>
 
       <varlistentry>
-        <term><varname>FairQueueTrafficPolicingBuckets=</varname></term>
+        <term><varname>Buckets=</varname></term>
         <listitem>
           <para>Specifies the size of the hash table used for flow lookups. Defaults to unset and
           kernel's default is used.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>FairQueueTrafficPolicingOrphanMask=</varname></term>
+        <term><varname>OrphanMask=</varname></term>
         <listitem>
           <para>Takes an unsigned integer. For packets not owned by a socket, fq is able to mask a part
           of hash and reduce number of buckets associated with the traffic. Defaults to unset and
       </varlistentry>
 
       <varlistentry>
-        <term><varname>FairQueueTrafficPolicingPacing=</varname></term>
+        <term><varname>Pacing=</varname></term>
         <listitem>
           <para>Takes a boolean, and enables or disables flow pacing. Defaults to unset and kernel's
           default is used.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>FairQueueTrafficPolicingCEThresholdSec=</varname></term>
+        <term><varname>CEThresholdSec=</varname></term>
         <listitem>
           <para>Takes a timespan. This sets a threshold above which all packets are marked with ECN
           Congestion Experienced (CE). Defaults to unset and kernel's default is used.</para>
         </listitem>
       </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[TrivialLinkEqualizer] Section Options</title>
+    <para>The <literal>[TrivialLinkEqualizer]</literal> section manages the queueing discipline (qdisc) of
+    trivial link equalizer (teql).</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>, <literal>ingress</literal> or a class id. The class id takes the
+          major and minor number in hexadecimal ranges 1 to ffff separated with a colon
+          (<literal>major:minor</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>
+
+      <varlistentry>
+        <term><varname>Id=</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>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[HierarchyTokenBucket] Section Options</title>
+    <para>The <literal>[HierarchyTokenBucket]</literal> section manages the queueing discipline (qdisc) of
+    hierarchy token bucket (htb).</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>, <literal>ingress</literal> or a class id. The class id takes the
+          major and minor number in hexadecimal ranges 1 to ffff separated with a colon
+          (<literal>major:minor</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>
+
+      <varlistentry>
+        <term><varname>DefaultClass=</varname></term>
+        <listitem>
+          <para>Takes the minor id in hexadecimal of the default class. Unclassified traffic gets sent
+          to the class. Defaults to unset.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[HierarchyTokenBucketClass] Section Options</title>
+    <para>The <literal>[HierarchyTokenBucketClass]</literal> section manages the traffic control class of
+    hierarchy token bucket (htb).</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>,
+          or a qdisc id. The qdisc id takes the major and minor number in hexadecimal ranges 1 to ffff
+          separated with a colon (<literal>major:minor</literal>). Defaults to <literal>root</literal>.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>ClassId=</varname></term>
+        <listitem>
+          <para>Specifies the major and minur number of unique identifier of the class, known as the
+          class ID. Each number is in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
+        </listitem>
+      </varlistentry>
+
+      <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. This setting is mandatory.</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>
 
     </variablelist>
   </refsect1>
index 39cca8cf514faf863112c2731f919d06d85e0423..f6fe3d83883eca518563535f522ed27c3d169503 100644 (file)
     limitations as inotify, and for example cannot be used to monitor
     files or directories changed by other machines on remote NFS file
     systems.</para>
+
+    <para>When a service unit triggered by a path unit terminates (regardless whether it exited successfully
+    or failed), monitored paths are checked immediately again, and the service accordingly restarted
+    instantly. As protection against busy looping in this trigger/start cycle, a start rate limit is enforced
+    on the service unit, see <varname>StartLimitIntervalSec=</varname> and
+    <varname>StartLimitBurst=</varname> in
+    <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>. Unlike
+    other service failures, the error condition that the start rate limit is hit is propagated from the
+    service unit to the path unit and causes the path unit to fail as well, thus ending the loop.</para>
   </refsect1>
 
   <refsect1>
index bf7aff6b2026787a8995e036269268372cf58d0e..7e116f8e835c6e3dc299686fb7ed7b867e13cdd7 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.resource-control">
+<refentry id="systemd.resource-control" xmlns:xi="http://www.w3.org/2001/XInclude">
   <refentryinfo>
     <title>systemd.resource-control</title>
     <productname>systemd</productname>
   <refsect1>
     <title>Unified and Legacy Control Group Hierarchies</title>
 
-    <para>The unified control group hierarchy is the new version of kernel control group interface, see <ulink
-    url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>. Depending on the resource type,
-    there are differences in resource control capabilities.  Also, because of interface changes, some resource types
-    have separate set of options on the unified hierarchy.</para>
+    <para>The unified control group hierarchy is the new version of kernel control group interface, see
+    <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html">Control Groups v2</ulink>.
+    Depending on the resource type, there are differences in resource control capabilities. Also, because of
+    interface changes, some resource types have separate set of options on the unified hierarchy.</para>
 
     <para>
       <variablelist>
     application.</para>
 
     <para>Legacy control group hierarchy (see <ulink
-    url="https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt">cgroups.txt</ulink>), also called cgroup-v1,
-    doesn't allow safe delegation of controllers to unprivileged processes. If the system uses the legacy control group
-    hierarchy, resource control is disabled for systemd user instance, see
-    <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
-    </para>
+    url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/">Control Groups version 1</ulink>),
+    also called cgroup-v1, doesn't allow safe delegation of controllers to unprivileged processes. If the
+    system uses the legacy control group hierarchy, resource control is disabled for the systemd user
+    instance, see
+    <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
   </refsect1>
 
   <refsect1>
           is used on the system. These options take an integer value and control the <literal>cpu.weight</literal>
           control group attribute. The allowed range is 1 to 10000. Defaults to 100. For details about this control
           group attribute, see <ulink
-          url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink> and <ulink
-          url="https://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt">sched-design-CFS.txt</ulink>.
+          url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html">Control Groups v2</ulink> and <ulink
+          url="https://www.kernel.org/doc/html/latest/scheduler/sched-design-CFS.html">CFS Scheduler</ulink>.
           The available CPU time is split up among all units within one slice relative to their CPU time weight.</para>
 
           <para>While <varname>StartupCPUWeight=</varname> only applies to the startup phase of the system,
           available on one CPU. Use values &gt; 100% for allotting CPU time on more than one CPU. This controls the
           <literal>cpu.max</literal> attribute on the unified control group hierarchy and
           <literal>cpu.cfs_quota_us</literal> on legacy. For details about these control group attributes, see <ulink
-          url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink> and <ulink
+          url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html">Control Groups v2</ulink> and <ulink
           url="https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt">sched-bwc.txt</ulink>.</para>
 
           <para>Example: <varname>CPUQuota=20%</varname> ensures that the executed processes will never get more than
 
           <para>This controls the second field of <literal>cpu.max</literal> attribute on the unified control group hierarchy
           and <literal>cpu.cfs_period_us</literal> on legacy. For details about these control group attributes, see
-          <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink> and
-          <ulink url="https://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt">sched-design-CFS.txt</ulink>.</para>
+          <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html">Control Groups v2</ulink> and
+          <ulink url="https://www.kernel.org/doc/html/latest/scheduler/sched-design-CFS.html">CFS Scheduler</ulink>.</para>
 
           <para>Example: <varname>CPUQuotaPeriodSec=10ms</varname> to request that the CPU quota is measured in periods of 10ms.</para>
         </listitem>
           useful in order to always inherit all of the protection afforded by ancestors.
           This controls the <literal>memory.min</literal> control group attribute. For details about this
           control group attribute, see <ulink
-          url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
+          url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
 
           <para>This setting is supported only if the unified control group hierarchy is used and disables
           <varname>MemoryLimit=</varname>.</para>
           useful in order to always inherit all of the protection afforded by ancestors.
           This controls the <literal>memory.low</literal> control group attribute. For details about this
           control group attribute, see <ulink
-          url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
+          url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
 
           <para>This setting is supported only if the unified control group hierarchy is used and disables
           <varname>MemoryLimit=</varname>.</para>
           system. If assigned the
           special value <literal>infinity</literal>, no memory throttling is applied. This controls the
           <literal>memory.high</literal> control group attribute. For details about this control group attribute, see
-          <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
+          <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
 
           <para>This setting is supported only if the unified control group hierarchy is used and disables
           <varname>MemoryLimit=</varname>.</para>
           percentage value may be specified, which is taken relative to the installed physical memory on the system. If
           assigned the special value <literal>infinity</literal>, no memory limit is applied. This controls the
           <literal>memory.max</literal> control group attribute. For details about this control group attribute, see
-          <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
+          <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
 
           <para>This setting replaces <varname>MemoryLimit=</varname>.</para>
         </listitem>
           parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. If assigned the
           special value <literal>infinity</literal>, no swap limit is applied. This controls the
           <literal>memory.swap.max</literal> control group attribute. For details about this control group attribute,
-          see <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
+          see <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
 
           <para>This setting is supported only if the unified control group hierarchy is used and disables
           <varname>MemoryLimit=</varname>.</para>
           of tasks or a percentage value that is taken relative to the configured maximum number of tasks on the
           system.  If assigned the special value <literal>infinity</literal>, no tasks limit is applied. This controls
           the <literal>pids.max</literal> control group attribute. For details about this control group attribute, see
-          <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v1/pids.txt">pids.txt</ulink>.</para>
+          <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/pids.html">Process Number Controller</ulink>.
+          </para>
 
-          <para>The
-          system default for this setting may be controlled with
+          <para>The system default for this setting may be controlled with
           <varname>DefaultTasksMax=</varname> in
           <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
         </listitem>
           hierarchy is used on the system. Takes a single weight value (between 1 and 10000) to set the default block
           I/O weight. This controls the <literal>io.weight</literal> control group attribute, which defaults to
           100. For details about this control group attribute, see <ulink
-          url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.  The available I/O
-          bandwidth is split up among all units within one slice relative to their block I/O weight.</para>
+          url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#io-interface-files">IO Interface Files</ulink>.
+          The available I/O bandwidth is split up among all units within one slice relative to their block
+          I/O weight.</para>
 
           <para>While <varname>StartupIOWeight=</varname> only applies
           to the startup phase of the system,
           device of the file system of the file is determined. This controls the <literal>io.weight</literal> control
           group attribute, which defaults to 100. Use this option multiple times to set weights for multiple devices.
           For details about this control group attribute, see <ulink
-          url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
+          url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#io-interface-files">IO Interface Files</ulink>.</para>
 
           <para>This setting replaces <varname>BlockIODeviceWeight=</varname> and disables settings prefixed with
           <varname>BlockIO</varname> or <varname>StartupBlockIO</varname>.</para>
+
+          <para>The specified device node should reference a block device that has an I/O scheduler
+          associated, i.e. should not refer to partition or loopback block devices, but to the originating,
+          physical device. When a path to a regular file or directory is specified it is attempted to
+          discover the correct originating device backing the file system of the specified path. This works
+          correctly only for simpler cases, where the file system is directly placed on a partition or
+          physical block device, or where simple 1:1 encryption using dm-crypt/LUKS is used. This discovery
+          does not cover complex storage and in particular RAID and volume management storage devices.</para>
         </listitem>
       </varlistentry>
 
           "/dev/disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0 5M"). This controls the <literal>io.max</literal> control
           group attributes. Use this option multiple times to set bandwidth limits for multiple devices. For details
           about this control group attribute, see <ulink
-          url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.
+          url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#io-interface-files">IO Interface Files</ulink>.
           </para>
 
           <para>These settings replace <varname>BlockIOReadBandwidth=</varname> and
           <varname>BlockIOWriteBandwidth=</varname> and disable settings prefixed with <varname>BlockIO</varname> or
           <varname>StartupBlockIO</varname>.</para>
+
+          <para>Similar restrictions on block device discovery as for <varname>IODeviceWeight=</varname> apply, see above.</para>
         </listitem>
       </varlistentry>
 
           "/dev/disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0 1K"). This controls the <literal>io.max</literal> control
           group attributes. Use this option multiple times to set IOPS limits for multiple devices. For details about
           this control group attribute, see <ulink
-          url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.
+          url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#io-interface-files">IO Interface Files</ulink>.
           </para>
 
           <para>These settings are supported only if the unified control group hierarchy is used and disable settings
           prefixed with <varname>BlockIO</varname> or <varname>StartupBlockIO</varname>.</para>
+
+          <para>Similar restrictions on block device discovery as for <varname>IODeviceWeight=</varname> apply, see above.</para>
         </listitem>
       </varlistentry>
 
           system of the file is determined. This controls the <literal>io.latency</literal> control group
           attribute. Use this option multiple times to set latency target for multiple devices. For details about this
           control group attribute, see <ulink
-          url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
+          url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#io-interface-files">IO Interface Files</ulink>.</para>
 
           <para>Implies <literal>IOAccounting=yes</literal>.</para>
 
           <para>These settings are supported only if the unified control group hierarchy is used.</para>
+
+          <para>Similar restrictions on block device discovery as for <varname>IODeviceWeight=</varname> apply, see above.</para>
         </listitem>
       </varlistentry>
 
           (<emphasis>m</emphasis>knod), respectively. On cgroup-v1 this controls the
           <literal>devices.allow</literal> control group attribute. For details about this control group
           attribute, see <ulink
-          url="https://www.kernel.org/doc/Documentation/cgroup-v1/devices.txt">devices.txt</ulink>. On
-          cgroup-v2 this functionality is implemented using eBPF filtering.</para>
+          url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/devices.html">Device Whitelist Controller</ulink>.
+          In the unified cgroup hierarchy this functionality is implemented using eBPF filtering.</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
@@ -832,9 +847,9 @@ DeviceAllow=/dev/loop-control
           hierarchy. Accordingly, access to the specified controllers will not be granted to unprivileged services on
           the legacy hierarchy, even when requested.</para>
 
-          <para>The following controller names may be specified: <option>cpu</option>, <option>cpuacct</option>,
-          <option>io</option>, <option>blkio</option>, <option>memory</option>, <option>devices</option>,
-          <option>pids</option>. Not all of these controllers are available on all kernels however, and some are
+          <xi:include href="supported-controllers.xml"  xpointer="controllers-text" />
+
+          <para>Not all of these controllers are available on all kernels however, and some are
           specific to the unified hierarchy while others are specific to the legacy hierarchy. Also note that the
           kernel might support further controllers, which aren't covered here yet as delegation is either not supported
           at all for them or not defined cleanly.</para>
@@ -861,8 +876,7 @@ DeviceAllow=/dev/loop-control
           to disable. Passing <varname>DisableControllers=</varname> by itself with no controller name present resets
           the disabled controller list.</para>
 
-          <para>Valid controllers are <option>cpu</option>, <option>cpuacct</option>, <option>io</option>,
-          <option>blkio</option>, <option>memory</option>, <option>devices</option>, and <option>pids</option>.</para>
+          <xi:include href="supported-controllers.xml"  xpointer="controllers-text" />
         </listitem>
       </varlistentry>
     </variablelist>
@@ -883,7 +897,7 @@ DeviceAllow=/dev/loop-control
           <para>Assign the specified CPU time share weight to the processes executed. These options take an integer
           value and control the <literal>cpu.shares</literal> control group attribute. The allowed range is 2 to
           262144. Defaults to 1024. For details about this control group attribute, see <ulink
-          url="https://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt">sched-design-CFS.txt</ulink>.
+          url="https://www.kernel.org/doc/html/latest/scheduler/sched-design-CFS.html">CFS Scheduler</ulink>.
           The available CPU time is split up among all units within one slice relative to their CPU time share
           weight.</para>
 
@@ -911,7 +925,7 @@ DeviceAllow=/dev/loop-control
           <literal>infinity</literal>, no memory limit is applied. This controls the
           <literal>memory.limit_in_bytes</literal> control group attribute. For details about this control group
           attribute, see <ulink
-          url="https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt">memory.txt</ulink>.</para>
+          url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/memory.html">Memory Resource Controller</ulink>.</para>
 
           <para>Implies <literal>MemoryAccounting=yes</literal>.</para>
 
@@ -942,7 +956,7 @@ DeviceAllow=/dev/loop-control
         group hierarchy is used on the system. Takes a single weight value (between 10 and 1000) to set the default
         block I/O weight. This controls the <literal>blkio.weight</literal> control group attribute, which defaults to
         500. For details about this control group attribute, see <ulink
-        url="https://www.kernel.org/doc/Documentation/cgroup-v1/blkio-controller.txt">blkio-controller.txt</ulink>.
+        url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/blkio-controller.html">Block IO Controller</ulink>.
         The available I/O bandwidth is split up among all units within one slice relative to their block I/O
         weight.</para>
 
@@ -973,7 +987,7 @@ DeviceAllow=/dev/loop-control
           file system of the file is determined. This controls the <literal>blkio.weight_device</literal> control group
           attribute, which defaults to 1000. Use this option multiple times to set weights for multiple devices. For
           details about this control group attribute, see <ulink
-          url="https://www.kernel.org/doc/Documentation/cgroup-v1/blkio-controller.txt">blkio-controller.txt</ulink>.</para>
+          url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/blkio-controller.html">Block IO Controller</ulink>.</para>
 
           <para>Implies
           <literal>BlockIOAccounting=yes</literal>.</para>
@@ -997,7 +1011,7 @@ DeviceAllow=/dev/loop-control
           <literal>blkio.throttle.read_bps_device</literal> and <literal>blkio.throttle.write_bps_device</literal>
           control group attributes. Use this option multiple times to set bandwidth limits for multiple devices. For
           details about these control group attributes, see <ulink
-          url="https://www.kernel.org/doc/Documentation/cgroup-v1/blkio-controller.txt">blkio-controller.txt</ulink>.
+          url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/blkio-controller.html">Block IO Controller</ulink>.
           </para>
 
           <para>Implies
@@ -1027,11 +1041,7 @@ DeviceAllow=/dev/loop-control
       <citerefentry><refentrytitle>systemd.directives</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
       The documentation for control groups and specific controllers in the Linux kernel:
-      <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt">cgroups.txt</ulink>,
-      <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt">cpuacct.txt</ulink>,
-      <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt">memory.txt</ulink>,
-      <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v1/blkio-controller.txt">blkio-controller.txt</ulink>.
-      <ulink url="https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt">sched-bwc.txt</ulink>.
+      <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html">Control Groups v2</ulink>.
     </para>
   </refsect1>
 </refentry>
index daf3554db2b663b457e93acd576c2188d2eb3417..b624ac5f9303990683bf502ee1672dd2e4dcc80d 100644 (file)
     url="https://www.freedesktop.org/wiki/Software/systemd/ControlGroupInterface/">New
     Control Group Interfaces</ulink> for an introduction on how to make
     use of scope units from programs.</para>
+
+    <para>Note that, unlike service units, scope units have no "main" process: all processes in the scope are
+    equivalent. The lifecycle of the scope unit is thus not bound to the lifetime of one specific process,
+    but to the existence of at least one process in the scope. This also means that the exit statuses of
+    these processes are not relevant for the scope unit failure state. Scope units may still enter a failure
+    state, for example due to resource exhaustion or stop timeouts being reached, but not due to programs
+    inside of them terminating uncleanly. Since processes managed as scope units generally remain children of
+    the original process that forked them off, it is also the job of that process to collect their exit
+    statuses and act on them as needed.</para>
   </refsect1>
 
   <refsect1>
index c839af1842c85832519f61f509f40911b7651d9f..d54a5de8715b53d63fcd6cd8bea6d40c60e56a2a 100644 (file)
@@ -26,6 +26,7 @@
     <filename>cryptsetup-pre.target</filename>,
     <filename>cryptsetup.target</filename>,
     <filename>ctrl-alt-del.target</filename>,
+    <filename>blockdev@.target</filename>,
     <filename>boot-complete.target</filename>,
     <filename>default.target</filename>,
     <filename>emergency.target</filename>,
       not useful as only unit within a transaction.</para>
 
       <variablelist>
+        <varlistentry>
+          <term><filename>blockdev@.target</filename></term>
+          <listitem><para>This template unit is used to order mount units and other consumers of block
+          devices after services that synthesize these block devices. In particular, this is intended to be
+          used with storage services (such as
+          <citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>)
+          that allocate and manage a virtual block device. Storage services are ordered before an instance of
+          <filename>blockdev@.target</filename>, and the consumer units after it. The ordering is
+          particularly relevant during shutdown, as it ensures that the mount is deactivated first and the
+          service backing the mount later. The <filename>blockdev@.target</filename> instance should be
+          pulled in via a <option>Wants=</option> dependency of the storage daemon and thus generally not be
+          part of any transaction unless a storage daemon is used. The instance name for instances of this
+          template unit must be a properly escaped block device node path, e.g.
+          <filename>blockdev@dev-mapper-foobar.target</filename> for the storage device
+          <filename>/dev/mapper/foobar</filename>.</para></listitem>
+        </varlistentry>
         <varlistentry>
           <term><filename>cryptsetup-pre.target</filename></term>
           <listitem>
index 05786cc534a1edadffef8ef8d69baa2960f1176d..04b1564b1747e8bf62f0c985354cae431b3471ce 100644 (file)
       <citerefentry><refentrytitle>systemd.nspawn</refentrytitle><manvolnum>5</manvolnum></citerefentry>
       </para></listitem>
 
+      <listitem><para>link files, see
+      <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+      </para></listitem>
+
+      <listitem><para>netdev and network files, see
+      <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+      </para></listitem>
+
       <listitem><para>daemon config files, see
       <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-user.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
index ebf6de4eb233cc5dc4b63aea18af0c2c4e897129..040b8e28939eadd20bbc887823f67c21e0c008e7 100644 (file)
         timers defined in the other directives.</para>
 
         <para>These are monotonic timers, independent of wall-clock time and timezones. If the computer is
-        temporarily suspended, the monotonic clock pauses, too.</para>
+        temporarily suspended, the monotonic clock generally pauses, too. Note that if
+        <varname>WakeSystem=</varname> is used, a different monotonic clock is selected that continues to
+        advance while the system is suspended and thus can be used as the trigger to resume the
+        system.</para>
 
         <para>If the empty string is assigned to any of these options, the list of timers is reset (both
         monotonic timers and <varname>OnCalendar=</varname> timers, see below), and all prior assignments
         <citerefentry><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>
         for details. To optimize power consumption, make sure to set
         this value as high as possible and as low as
-        necessary.</para></listitem>
+        necessary.</para>
+
+        <para>Note that this setting is primarily a power saving option that allows coalescing CPU
+        wake-ups. It should not be confused with <varname>RandomizedDelaySec=</varname> (see below) which
+        adds a random value to the time the timer shall elapse next and whose purpose is the opposite: to
+        stretch elapsing of timer events over a longer period to reduce workload spikes. For further details
+        and explanations and how both settings play together, see below.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <varname>false</varname>.</para>
 
         <para>Note that this functionality requires privileges and is thus generally only available in the
-        system service manager.</para></listitem>
+        system service manager.</para>
+
+        <para>Note that behaviour of monotonic clock timers (as configured with
+        <varname>OnActiveSec=</varname>, <varname>OnBootSec=</varname>, <varname>OnStartupSec=</varname>,
+        <varname>OnUnitActiveSec=</varname>, <varname>OnUnitInactiveSec=</varname>, see above) is altered
+        depending on this option. If false, a monotonic clock is used that is paused during system suspend
+        (<constant>CLOCK_MONOTONIC</constant>), if true a different monotonic clock is used that continues
+        advancing during system suspend (<constant>CLOCK_BOOTTIME</constant>), see
+        <citerefentry><refentrytitle>clock_getres</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
+        details.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index debb0c0d6c17aa848519889c4e57735bad941668..64240208c2bdac0c6d8ba11059e6410c54894b27 100644 (file)
         <para>Units listed in this option will be started if the configuring unit is. However, if the listed
         units fail to start or cannot be added to the transaction, this has no impact on the validity of the
         transaction as a whole, and this unit will still be started. This is the recommended way to hook
-        start-up of one unit to the start-up of another unit.</para>
+        the start-up of one unit to the start-up of another unit.</para>
 
         <para>Note that requirement dependencies do not influence the order in which services are started or
         stopped. This has to be configured independently with the <varname>After=</varname> or
         <listitem><para>These two settings expect a space-separated list of unit names. They may be specified
         more than once, in which case dependencies for all listed names are created.</para>
 
-        <para>Those two setttings configure ordering dependencies between units. If unit
+        <para>Those two settings configure ordering dependencies between units. If unit
         <filename>foo.service</filename> contains the setting <option>Before=bar.service</option> and both
         units are being started, <filename>bar.service</filename>'s start-up is delayed until
         <filename>foo.service</filename> has finished starting up. <varname>After=</varname> is the inverse
         configured by <varname>Requires=</varname>, <varname>Wants=</varname>, <varname>Requisite=</varname>,
         or <varname>BindsTo=</varname>. It is a common pattern to include a unit name in both the
         <varname>After=</varname> and <varname>Wants=</varname> options, in which case the unit listed will
-        be started before the unit that is configured with these options.</para></listitem>
+        be started before the unit that is configured with these options.</para>
+
+        <para>Note that <varname>Before=</varname> dependencies on device units have no effect and are not
+        supported.  Devices generally become available as a result of an external hotplug event, and systemd
+        creates the corresponding device unit without delay.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 8c8dc0bd75c1fa561a25fd505a6107da01d82f02..94b3845a0e84ddd27e5e2b39dd93a7618875c4ce 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>
 
       <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.show_status</varname></term>
 
-        <listitem><para>Takes a boolean argument or the constant
-        <constant>auto</constant>. Can be also specified without an argument, with
-        the same effect as a positive boolean.  If enabled, the systemd manager (PID
-        1) shows terse service status updates on the console during bootup.
-        <constant>auto</constant> behaves like <option>false</option> until a unit
-        fails or there is a significant delay in boot. Defaults to enabled, unless
-        <option>quiet</option> is passed as kernel command line option, in which case
-        it defaults to <constant>auto</constant>. If specified overrides the system
-        manager configuration file option <option>ShowStatus=</option>, see
+        <listitem><para>Takes a boolean argument or the constants <constant>error</constant> and
+        <constant>auto</constant>. Can be also specified without an argument, with the same effect as a
+        positive boolean. If enabled, the systemd manager (PID 1) shows terse service status updates on the
+        console during bootup. With <constant>error</constant>, only messages about failures are shown, but
+        boot is otherwise quiet. <constant>auto</constant> behaves like <option>false</option> until there is
+        a significant delay in boot. Defaults to enabled, unless <option>quiet</option> is passed as kernel
+        command line option, in which case it defaults to <constant>error</constant>. If specified overrides
+        the system manager configuration file option <option>ShowStatus=</option>, see
         <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
         </para></listitem>
       </varlistentry>
 
         <listitem><para>When specified without an argument or with a true argument,
         enables the usage of
-        <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">unified cgroup hierarchy</ulink>
+        <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html">unified cgroup hierarchy</ulink>
         (a.k.a. cgroups-v2). When specified with a false argument, fall back to
         hybrid or full legacy cgroup hierarchy.</para>
 
index 2e93715be6fc11b03ceaed8fe414062af8bac8aa..c632e8713e97d9690d54979d2d32ba79ca670966 100644 (file)
     <para><filename>/etc/sysusers.d/*.conf</filename></para>
     <para><filename>/run/sysusers.d/*.conf</filename></para>
     <para><filename>/usr/lib/sysusers.d/*.conf</filename></para>
+
+    <programlisting>
+#Type Name       ID                   GECOS                 Home directory Shell
+u     user_name  uid                  "User Description"    /path/to/shell
+u     user_name  uid:gid              -                     -
+u     user_name  /file/owned/by/user  -                     -
+g     group_name gid                  "Group Description"
+g     group_name /file/owned/by/group -
+m     user_name  group_name
+r     -          lowest-highest</programlisting>
   </refsynopsisdiv>
 
   <refsect1>
 
     <programlisting>#Type Name     ID             GECOS                 Home directory Shell
 u     httpd    404            "HTTP User"
-u     authd    /usr/bin/authd "Authorization user"
+u     _authd   /usr/bin/authd "Authorization user"
 u     postgres -              "Postgresql Database" /var/lib/pgsql /usr/libexec/postgresdb
 g     input    -              -
-m     authd    input
-u     root     0              "Superuser"           /root          /bin/zsh</programlisting>
+m     _authd   input
+u     root     0              "Superuser"           /root          /bin/zsh
+r     -        500-900
+</programlisting>
 
     <para>Empty lines and lines beginning with the <literal>#</literal> character are ignored, and may be used for
     commenting.</para>
@@ -101,15 +113,15 @@ u     root     0              "Superuser"           /root          /bin/zsh</pro
           <term><varname>u</varname></term>
           <listitem><para>Create a system user and group of the specified name should
           they not exist yet. The user's primary group will be set to the group
-          bearing the same name. The account will be created disabled, so that logins
-          are not allowed.</para></listitem>
+          bearing the same name unless the ID field specifies it. The account will be
+          created disabled, so that logins are not allowed.</para></listitem>
         </varlistentry>
 
         <varlistentry>
           <term><varname>g</varname></term>
           <listitem><para>Create a system group of the specified name
           should it not exist yet. Note that <varname>u</varname>
-          implicitly create a matching group. The group will be
+          implicitly creates a matching group. The group will be
           created with no password set.</para></listitem>
         </varlistentry>
 
@@ -166,9 +178,10 @@ u     root     0              "Superuser"           /root          /bin/zsh</pro
       path's owner/group. This is useful to create users whose UID/GID
       match the owners of pre-existing files (such as SUID or SGID
       binaries).
-      The syntax <literal><replaceable>uid</replaceable>:<replaceable>gid</replaceable></literal> is also supported to
-      allow creating user and group pairs with different numeric UID and GID values. The group with the indicated GID must get created explicitly before or it must already exist. Specifying <literal>-</literal> for the UID in this syntax
-      is also supported.
+      The syntaxes <literal><replaceable>uid</replaceable>:<replaceable>gid</replaceable></literal> and
+      <literal><replaceable>uid</replaceable>:<replaceable>groupname</replaceable></literal> are supported to
+      allow creating users with specific primary groups. The given group must be created explicitly, or it
+      must already exist. Specifying <literal>-</literal> for the UID in these syntaxes is also supported.
       </para>
 
       <para>For <varname>m</varname> lines, this field should contain
index bfc83db065c41e629e289e68a619ea9ef4ad6bcf..a29c913ff078c682b8ce7932c866fb00bcbe63b3 100644 (file)
@@ -46,7 +46,8 @@ d     /directory/to/create-and-cleanup         mode user group cleanup-age -
 D     /directory/to/create-and-remove          mode user group cleanup-age -
 e     /directory/to/cleanup                    mode user group cleanup-age -
 v     /subvolume-or-directory/to/create        mode user group -           -
-Q     /subvolume/to/create                     mode user group -           -
+q     /subvolume-or-directory/to/create        mode user group -           -
+Q     /subvolume-or-directory/to/create        mode user group -           -
 p     /fifo/to/create                          mode user group -           -
 p+    /fifo/to/[re]create                      mode user group -           -
 L     /symlink/to/create                       -    -    -     -           symlink/target/path
@@ -60,8 +61,8 @@ x     /path-or-glob/to/ignore                  -    -    -     -           -
 X     /path-or-glob/to/ignore/recursively      -    -    -     -           -
 r     /empty/dir/to/remove                     -    -    -     -           -
 R     /dir/to/remove/recursively               -    -    -     -           -
-z     /path-or-glob/to/adjust/mode             mode user group -           MAC context
-Z     /path-or-glob/to/adjust/mode/recursively mode user group -           MAC context
+z     /path-or-glob/to/adjust/mode             mode user group -           -
+Z     /path-or-glob/to/adjust/mode/recursively mode user group -           -
 t     /path-or-glob/to/set/xattrs              -    -    -     -           xattrs
 T     /path-or-glob/to/set/xattrs/recursively  -    -    -     -           xattrs
 h     /path-or-glob/to/set/attrs               -    -    -     -           file attrs
@@ -93,7 +94,7 @@ A+    /path-or-glob/to/append/acls/recursively -    -    -     -           POSIX
     <filename>systemd-tmpfiles-cleanup.service</filename>, and associated units.</para>
 
     <para>System daemons frequently require private runtime directories below <filename>/run</filename> to
-    store communication sockets and similar. For these, is is better to use
+    store communication sockets and similar. For these, it is better to use
     <varname>RuntimeDirectory=</varname> in their unit files (see
     <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
     details), if the flexibility provided by <filename>tmpfiles.d</filename> is not required. The advantages
index 09254f818ec16775e40a06bd4a87bc63d4dfca83..9b97ee63d91d943d629670ab87d8a1b8acdb6b5d 100644 (file)
             that is enforced on <filename>systemd-udevd.service</filename>.</para>
             <para>Please also note that <literal>:=</literal> and <literal>=</literal> are clearing
             both, program and builtin commands.</para>
+            <para>In order to activate long-running processes from udev rules, provide a service unit, and
+            pull it in from a udev device using the <varname>SYSTEMD_WANTS</varname> device property. See
+            <citerefentry><refentrytitle>systemd.device</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+            for details.</para>
           </listitem>
         </varlistentry>
 
index 81a6fef4baa23b84217e67e3f31644841da7ff6e..f4603df073965abc39862972938da4a04ef7c99d 100644 (file)
@@ -49,7 +49,7 @@
     <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,
     for example by
-    <citerefentry><refentrytitle>sshd</refentrytitle><manvolnum>8</manvolnum></citerefentry> or a
+    <citerefentry project='die-net'><refentrytitle>sshd</refentrytitle><manvolnum>8</manvolnum></citerefentry> or a
     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
@@ -142,7 +142,7 @@ Control group /:
 …</programlisting>
       <para>User with UID 1000 is logged in using <command>gdm</command> (<filename
       index="false">session-4.scope</filename>) and
-      <citerefentry><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+      <citerefentry project='die-net'><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry>
       (<filename index="false">session-19.scope</filename>), and also has a user manager instance
       running (<filename index="false">user@1000.service</filename>).  User with UID 1001 is logged
       in using <command>ssh</command> (<filename index="false">session-20.scope</filename>) and
diff --git a/man/userdbctl.xml b/man/userdbctl.xml
new file mode 100644 (file)
index 0000000..606ce67
--- /dev/null
@@ -0,0 +1,258 @@
+<?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="userdbctl" conditional='ENABLE_USERDB'
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>userdbctl</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>userdbctl</refentrytitle>
+    <manvolnum>1</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>userdbctl</refname>
+    <refpurpose>Inspect users, groups and group memberships</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>userdbctl</command>
+      <arg choice="opt" rep="repeat">OPTIONS</arg>
+      <arg choice="req">COMMAND</arg>
+      <arg choice="opt" rep="repeat">NAME</arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>userdbctl</command> may be used to inspect user and groups (as well as group memberships)
+    of the system. This client utility inquires user/group information provided by various system services,
+    both operating on JSON user/group records (as defined by the <ulink
+    url="https://systemd.io/USER_RECORD">JSON User Record</ulink> and <ulink
+    url="https://systemd.io/GROUP_RECORD">JSON Group Record</ulink> definitions), and classic UNIX NSS/glibc
+    user and group records. This tool is primarily a client to the <ulink
+    url="https://systemd.io/USER_GROUP_API">User/Group Record Lookup API via Varlink</ulink>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+
+    <para>The following options are understood:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><option>--output=</option><replaceable>MODE</replaceable></term>
+
+        <listitem><para>Choose the output mode, takes one of <literal>classic</literal>,
+        <literal>friendly</literal>, <literal>table</literal>, <literal>json</literal>. If
+        <literal>classic</literal>, an output very close to the format of <filename>/etc/passwd</filename> or
+        <filename>/etc/group</filename> is generated. If <literal>friendly</literal> a more comprehensive and
+        user friendly, human readable output is generated; if <literal>table</literal> a minimal, tabular
+        output is generated; if <literal>json</literal> a JSON formatted output is generated. Defaults to
+        <literal>friendly</literal> if a user/group is specified on the command line,
+        <literal>table</literal> otherwise.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--service=</option><replaceable>SERVICE</replaceable><optional>:<replaceable>SERVICE…</replaceable></optional></term>
+        <term><option>-s</option> <replaceable>SERVICE</replaceable>:<replaceable>SERVICE…</replaceable></term>
+
+        <listitem><para>Controls which services to query for users/groups. Takes a list of one or more
+        service names, separated by <literal>:</literal>. See below for a list of well-known service
+        names. If not specified all available services are queried at once.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--with-nss=</option><replaceable>BOOL</replaceable></term>
+
+        <listitem><para>Controls whether to include classic glibc/NSS user/group lookups in the output. If
+        <option>--with-nss=no</option> is used any attempts to resolve or enumerate users/groups provided
+        only via glibc NSS is suppressed. If <option>--with-nss=yes</option> is specified such users/groups
+        are included in the output (which is the default).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--synthesize=</option><replaceable>BOOL</replaceable></term>
+
+        <listitem><para>Controls whether to synthesize records for the root and nobody users/groups if they
+        aren't defined otherwise. By default (or <literal>yes</literal>) such records are implicitly
+        synthesized if otherwise missing since they have special significance to the OS. When
+        <literal>no</literal> this synthesizing is turned off.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-N</option></term>
+
+        <listitem><para>This option is short for <option>--with-nss=no</option>
+        <option>--synthesize=no</option>. Use this option to show only records that are natively defined as
+        JSON user or group records, with all NSS/glibc compatibility and all implicit synthesis turned
+        off.</para></listitem>
+      </varlistentry>
+
+      <xi:include href="standard-options.xml" xpointer="no-pager" />
+      <xi:include href="standard-options.xml" xpointer="no-legend" />
+      <xi:include href="standard-options.xml" xpointer="help" />
+      <xi:include href="standard-options.xml" xpointer="version" />
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Commands</title>
+
+    <para>The following commands are understood:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><command>user</command> <optional><replaceable>USER</replaceable>…</optional></term>
+
+        <listitem><para>List all known users records or show details of one or more specified user
+        records. Use <option>--output=</option> to tweak output mode.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>group</command> <optional><replaceable>GROUP</replaceable>…</optional></term>
+
+        <listitem><para>List all known group records or show details of one or more specified group
+        records. Use <option>--output=</option> to tweak output mode.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>users-in-group</command> <optional><replaceable>GROUP</replaceable>…</optional></term>
+
+        <listitem><para>List users that are members of the specified groups. If no groups are specified list
+        all user/group memberships defined. Use <option>--output=</option> to tweak output
+        mode.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>groups-of-user</command> <optional><replaceable>USER</replaceable>…</optional></term>
+
+        <listitem><para>List groups that the specified users are members of. If no users are specified list
+        all user/group memberships defined (in this case <command>groups-of-user</command> and
+        <command>users-in-group</command> are equivalent). Use <option>--output=</option> to tweak output
+        mode.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>services</command></term>
+
+        <listitem><para>List all services currently providing user/group definitions to the system. See below
+        for a list of well-known services providing user information.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>ssh-authorized-keys</command></term>
+
+        <listitem><para>This operation is not a public, user-facing interface. It is used to allow the SSH daemon to pick
+        up authorized keys from user records, see below.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <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>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><constant>io.systemd.DynamicUser</constant></term>
+
+        <listitem><para>This service is provided by the system service manager itself (i.e. PID 1) and
+        makes all users (and their groups) synthesized through the <varname>DynamicUser=</varname> setting in
+        service unit files available to the system (see
+        <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+        details about this setting).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>io.systemd.Home</constant></term>
+
+        <listitem><para>This service is provided by
+        <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        and makes all users (and their groups) belonging to home directories managed by that service
+        available to the system.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>io.systemd.Multiplexer</constant></term>
+
+        <listitem><para>This service is provided by
+        <citerefentry><refentrytitle>systemd-userdbd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        and multiplexes user/group look-ups to all other running lookup services. This is the primary entry point
+        for user/group record clients, as it simplifies client side implementation substantially since they
+        can ask a single service for lookups instead of asking all running services in parallel.
+        <command>userdbctl</command> uses this service preferably, too, unless <option>--with-nss=</option>
+        or <option>--service=</option> are used, in which case finer control over the services to talk to is
+        required.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>io.systemd.NameSeviceSwitch</constant></term>
+
+        <listitem><para>This service is (also) provided by
+        <citerefentry><refentrytitle>systemd-userdbd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        and converts classic NSS/glibc user and group records to JSON user/group records, providing full
+        backwards compatibility. Use <option>--with-nss=no</option> to disable this compatibility, see
+        above. Note that compatibility is actually provided in both directions:
+        <citerefentry><refentrytitle>nss-systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry> will
+        automatically synthesize classic NSS/glibc user/group records from all JSON user/group records
+        provided to the system, thus using both APIs is mostly equivalent and provides access to the same
+        data, however the NSS/glibc APIs necessarily expose a more reduced set of fields
+        only.</para></listitem>
+      </varlistentry>
+    </variablelist>
+
+    <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
+    databases will still work.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Integration with SSH</title>
+
+    <para>The <command>userdbctl</command> tool may be used to make the list of SSH authorized keys possibly
+    contained in a user record available to the SSH daemon for authentication. For that configure the
+    following in <citerefentry
+    project='die-net'><refentrytitle>sshd_config</refentrytitle><manvolnum>5</manvolnum></citerefentry>:</para>
+
+    <programlisting>…
+AuthorizedKeysCommand /usr/bin/userdbctl ssh-authorized-keys %u
+AuthorizedKeysCommandUser root
+…</programlisting>
+  </refsect1>
+
+  <refsect1>
+    <title>Exit status</title>
+
+    <para>On success, 0 is returned, a non-zero failure code otherwise.</para>
+  </refsect1>
+
+  <xi:include href="less-variables.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-userdbd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>nss-systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>getent</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index 3b3786bbdb512942ab416754f465a63a844decda..d6c09cb18ab39e7f16460733e78d090c4460cdfc 100644 (file)
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: LGPL-2.1+
 
 project('systemd', 'c',
-        version : '244',
+        version : '245',
         license : 'LGPLv2+',
         default_options: [
                 'c_std=gnu99',
@@ -13,19 +13,21 @@ project('systemd', 'c',
         meson_version : '>= 0.46',
        )
 
-libsystemd_version = '0.27.1'
-libudev_version = '1.6.16'
+libsystemd_version = '0.28.0'
+libudev_version = '1.6.17'
 
 # We need the same data in two different formats, ugh!
 # Also, for hysterical reasons, we use different variable
 # names, sometimes. Not all variables are included in every
 # set. Ugh, ugh, ugh!
 conf = configuration_data()
-conf.set('PROJECT_VERSION',        meson.project_version())
+conf.set('PROJECT_VERSION',        meson.project_version(),
+         description : 'Numerical project version (used where a simple number is expected)')
 
 substs = configuration_data()
 substs.set('PROJECT_URL',          'https://www.freedesktop.org/wiki/Software/systemd')
-substs.set('PROJECT_VERSION',      meson.project_version())
+substs.set('PROJECT_VERSION',      meson.project_version(),
+           description : 'Numerical project version (used where a simple number is expected)')
 
 # 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
@@ -212,7 +214,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'))
@@ -243,6 +244,8 @@ conf.set_quoted('SYSTEMD_EXPORT_PATH',                        join_paths(rootlib
 conf.set_quoted('VENDOR_KEYRING_PATH',                        join_paths(rootlibexecdir, 'import-pubring.gpg'))
 conf.set_quoted('USER_KEYRING_PATH',                          join_paths(pkgsysconfdir, 'import-pubring.gpg'))
 conf.set_quoted('DOCUMENT_ROOT',                              join_paths(pkgdatadir, 'gatewayd'))
+conf.set_quoted('SYSTEMD_HOMEWORK_PATH',                      join_paths(rootlibexecdir, 'systemd-homework'))
+conf.set_quoted('SYSTEMD_USERWORK_PATH',                      join_paths(rootlibexecdir, 'systemd-userwork'))
 conf.set10('MEMORY_ACCOUNTING_DEFAULT',                       memory_accounting_default)
 conf.set_quoted('MEMORY_ACCOUNTING_DEFAULT_YES_NO',           memory_accounting_default ? 'yes' : 'no')
 conf.set('STATUS_UNIT_FORMAT_DEFAULT',                        'STATUS_UNIT_FORMAT_' + status_unit_format_default.to_upper())
@@ -280,7 +283,6 @@ substs.set('userenvgeneratordir',                             userenvgeneratordi
 substs.set('systemshutdowndir',                               systemshutdowndir)
 substs.set('systemsleepdir',                                  systemsleepdir)
 substs.set('CERTIFICATEROOT',                                 get_option('certificate-root'))
-substs.set('SYSTEMCTL',                                       join_paths(rootbindir, 'systemctl'))
 substs.set('RANDOM_SEED',                                     join_paths(randomseeddir, 'random-seed'))
 substs.set('SYSTEM_SYSVINIT_PATH',                            sysvinit_path)
 substs.set('SYSTEM_SYSVRCND_PATH',                            sysvrcnd_path)
@@ -448,9 +450,6 @@ conf.set('_GNU_SOURCE', true)
 conf.set('__SANE_USERSPACE_TYPES__', true)
 conf.set10('HAVE_WSTRINGOP_TRUNCATION', has_wstringop_truncation)
 
-conf.set('SIZEOF_PID_T', cc.sizeof('pid_t', prefix : '#include <sys/types.h>'))
-conf.set('SIZEOF_UID_T', cc.sizeof('uid_t', prefix : '#include <sys/types.h>'))
-conf.set('SIZEOF_GID_T', cc.sizeof('gid_t', prefix : '#include <sys/types.h>'))
 conf.set('SIZEOF_DEV_T', cc.sizeof('dev_t', prefix : '#include <sys/types.h>'))
 conf.set('SIZEOF_INO_T', cc.sizeof('ino_t', prefix : '#include <sys/types.h>'))
 conf.set('SIZEOF_TIME_T', cc.sizeof('time_t', prefix : '#include <sys/time.h>'))
@@ -873,6 +872,27 @@ endif
 libmount = dependency('mount',
                       version : fuzzer_build ? '>= 0' : '>= 2.30')
 
+want_libfdisk = get_option('fdisk')
+if want_libfdisk != 'false' and not skip_deps
+        libfdisk = dependency('fdisk',
+                              required : want_libfdisk == 'true')
+        have = libfdisk.found()
+else
+        have = false
+        libfdisk = []
+endif
+conf.set10('HAVE_LIBFDISK', have)
+
+want_pwquality = get_option('pwquality')
+if want_pwquality != 'false' and not skip_deps
+        libpwquality = dependency('pwquality', required : want_pwquality == 'true')
+        have = libpwquality.found()
+else
+        have = false
+        libpwquality = []
+endif
+conf.set10('HAVE_PWQUALITY', have)
+
 want_seccomp = get_option('seccomp')
 if want_seccomp != 'false' and not skip_deps
         libseccomp = dependency('libseccomp',
@@ -1000,6 +1020,9 @@ if want_libcryptsetup != 'false' and not skip_deps
                                    version : '>= 2.0.1',
                                    required : want_libcryptsetup == 'true')
         have = libcryptsetup.found()
+
+        conf.set10('HAVE_CRYPT_SET_METADATA_SIZE',
+                   have and cc.has_function('crypt_set_metadata_size', dependencies : libcryptsetup))
 else
         have = false
         libcryptsetup = []
@@ -1279,6 +1302,18 @@ 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)
 
+want_repart = get_option('repart')
+if want_repart != 'false'
+        have = (conf.get('HAVE_OPENSSL') == 1 and
+                conf.get('HAVE_LIBFDISK') == 1)
+        if want_repart == 'true' and not have
+                error('repart support was requested, but dependencies are not available')
+        endif
+else
+        have = false
+endif
+conf.set10('ENABLE_REPART', have)
+
 want_importd = get_option('importd')
 if want_importd != 'false'
         have = (conf.get('HAVE_LIBCURL') == 1 and
@@ -1293,6 +1328,22 @@ else
 endif
 conf.set10('ENABLE_IMPORTD', have)
 
+want_homed = get_option('homed')
+if want_homed != 'false'
+        have = (conf.get('HAVE_OPENSSL') == 1 and
+                conf.get('HAVE_LIBFDISK') == 1 and
+                conf.get('HAVE_LIBCRYPTSETUP') == 1)
+        if want_homed == 'true' and not have
+                error('homed support was requested, but dependencies are not available')
+        endif
+else
+        have = false
+endif
+conf.set10('ENABLE_HOMED', have)
+
+have = have and conf.get('HAVE_PAM') == 1
+conf.set10('ENABLE_PAM_HOME', have)
+
 want_remote = get_option('remote')
 if want_remote != 'false'
         have_deps = [conf.get('HAVE_MICROHTTPD') == 1,
@@ -1322,6 +1373,7 @@ foreach term : ['utmp',
                 'localed',
                 'machined',
                 'portabled',
+                'userdb',
                 'networkd',
                 'timedated',
                 'timesyncd',
@@ -1534,10 +1586,13 @@ subdir('src/coredump')
 subdir('src/pstore')
 subdir('src/hostname')
 subdir('src/import')
+subdir('src/partition')
 subdir('src/kernel-install')
 subdir('src/locale')
 subdir('src/machine')
 subdir('src/portable')
+subdir('src/userdb')
+subdir('src/home')
 subdir('src/nspawn')
 subdir('src/resolve')
 subdir('src/timedate')
@@ -1564,7 +1619,7 @@ test_dlopen = executable(
         build_by_default : want_tests != 'false')
 
 foreach tuple : [['myhostname', 'ENABLE_NSS_MYHOSTNAME'],
-                 ['systemd',    'ENABLE_NSS_SYSTEMD'],
+                 ['systemd',    'ENABLE_NSS_SYSTEMD', 'src/nss-systemd/userdb-glue.c src/nss-systemd/userdb-glue.h'],
                  ['mymachines', 'ENABLE_NSS_MYMACHINES'],
                  ['resolve',    'ENABLE_NSS_RESOLVE']]
 
@@ -1575,9 +1630,14 @@ foreach tuple : [['myhostname', 'ENABLE_NSS_MYHOSTNAME'],
                 sym = 'src/nss-@0@/nss-@0@.sym'.format(module)
                 version_script_arg = join_paths(project_source_root, sym)
 
+                sources = ['src/nss-@0@/nss-@0@.c'.format(module)]
+                if tuple.length() > 2
+                        sources += tuple[2].split()
+                endif
+
                 nss = shared_library(
                         'nss_' + module,
-                        'src/nss-@0@/nss-@0@.c'.format(module),
+                        sources,
                         disable_mempool_c,
                         version : '2',
                         include_directories : includes,
@@ -1974,6 +2034,97 @@ if conf.get('ENABLE_PORTABLED') == 1
         public_programs += exe
 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)
+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)
+
+        if conf.get('HAVE_PAM') == 1
+                version_script_arg = join_paths(project_source_root, pam_systemd_home_sym)
+                pam_systemd = shared_library(
+                        'pam_systemd_home',
+                        pam_systemd_home_c,
+                        name_prefix : '',
+                        include_directories : includes,
+                        link_args : ['-shared',
+                                     '-Wl,--version-script=' + version_script_arg],
+                        link_with : [libsystemd_static,
+                                     libshared_static],
+                        dependencies : [threads,
+                                        libpam,
+                                        libpam_misc,
+                                        libcrypt],
+                        link_depends : pam_systemd_home_sym,
+                        install : true,
+                        install_dir : pamlibdir)
+        endif
+endif
+
 foreach alias : ['halt', 'poweroff', 'reboot', 'runlevel', 'shutdown', 'telinit']
         meson.add_install_script(meson_make_symlink,
                                  join_paths(rootbindir, 'systemctl'),
@@ -2140,10 +2291,19 @@ if conf.get('ENABLE_TIMEDATECTL') == 1
 endif
 
 if conf.get('ENABLE_TIMESYNCD') == 1
+        if get_option('link-timesyncd-shared')
+                timesyncd_link_with = [libshared]
+        else
+                timesyncd_link_with = [libsystemd_static,
+                                       libshared_static,
+                                       libjournal_client,
+                                       libbasic_gcrypt]
+        endif
+
         executable('systemd-timesyncd',
                    systemd_timesyncd_sources,
                    include_directories : includes,
-                   link_with : [libshared],
+                   link_with : [timesyncd_link_with],
                    dependencies : [threads,
                                    libm],
                    install_rpath : rootlibexecdir,
@@ -2153,7 +2313,7 @@ if conf.get('ENABLE_TIMESYNCD') == 1
         executable('systemd-time-wait-sync',
                    'src/time-wait-sync/time-wait-sync.c',
                    include_directories : includes,
-                   link_with : [libshared],
+                   link_with : [timesyncd_link_with],
                    install_rpath : rootlibexecdir,
                    install : true,
                    install_dir : rootlibexecdir)
@@ -2345,6 +2505,21 @@ if conf.get('ENABLE_BINFMT') == 1
                                  mkdir_p.format(join_paths(sysconfdir, 'binfmt.d')))
 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)
+endif
+
 if conf.get('ENABLE_VCONSOLE') == 1
         executable('systemd-vconsole-setup',
                    'src/vconsole/vconsole-setup.c',
@@ -3204,8 +3379,8 @@ if conf.get('ENABLE_EFI') == 1
                 status += [
                         'EFI machine type:                  @0@'.format(EFI_MACHINE_TYPE_NAME),
                         'EFI CC                             @0@'.format(' '.join(efi_cc)),
-                        'EFI lib directory:                 @0@'.format(efi_libdir),
-                        'EFI lds directory:                 @0@'.format(efi_ldsdir),
+                        'EFI lds:                           @0@'.format(efi_lds),
+                        'EFI crt0:                          @0@'.format(efi_crt0),
                         'EFI include directory:             @0@'.format(efi_incdir)]
         endif
 endif
@@ -3216,6 +3391,8 @@ missing = []
 foreach tuple : [
         ['libcryptsetup'],
         ['PAM'],
+        ['pwquality'],
+        ['libfdisk'],
         ['p11kit'],
         ['AUDIT'],
         ['IMA'],
@@ -3240,6 +3417,7 @@ foreach tuple : [
         ['libiptc'],
         ['elfutils'],
         ['binfmt'],
+        ['repart'],
         ['vconsole'],
         ['quotacheck'],
         ['tmpfiles'],
@@ -3252,6 +3430,8 @@ foreach tuple : [
         ['logind'],
         ['machined'],
         ['portabled'],
+        ['userdb'],
+        ['homed'],
         ['importd'],
         ['hostnamed'],
         ['timedated'],
@@ -3297,6 +3477,7 @@ foreach tuple : [
         ['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')],
 ]
 
         if tuple.length() >= 2
index 4d641c3671240fea9f428e595d1fbfb0c97179ff..4988d41ff3b2ff2ee74b82878fd7407f87ca4882 100644 (file)
@@ -18,6 +18,8 @@ option('link-systemctl-shared', type: 'boolean',
        description : 'link systemctl against libsystemd-shared.so')
 option('link-networkd-shared', type: 'boolean',
        description : 'link systemd-networkd and its helpers to libsystemd-shared.so')
+option('link-timesyncd-shared', type: 'boolean',
+       description : 'link systemd-timesyncd and its helpers to libsystemd-shared.so')
 option('static-libsystemd', type : 'combo',
        choices : ['false', 'true', 'pic', 'no-pic'],
        description : '''install a static library for libsystemd''')
@@ -80,6 +82,8 @@ option('environment-d', type : 'boolean',
        description : 'support for environment.d')
 option('binfmt', type : 'boolean',
        description : 'support for custom binary formats')
+option('repart', type : 'combo', choices : ['auto', 'true', 'false'],
+       description : 'install the systemd-repart tool')
 option('coredump', type : 'boolean',
        description : 'install the coredump handler')
 option('pstore', type : 'boolean',
@@ -94,6 +98,10 @@ option('machined', type : 'boolean',
        description : 'install the systemd-machined stack')
 option('portabled', type : 'boolean',
        description : 'install the systemd-portabled stack')
+option('userdb', type : 'boolean',
+       description : 'install the systemd-userdbd stack')
+option('homed', type : 'combo', choices : ['auto', 'true', 'false'],
+       description : 'install the systemd-homed stack')
 option('networkd', type : 'boolean',
        description : 'install the systemd-networkd stack')
 option('timedated', type : 'boolean',
@@ -258,10 +266,14 @@ option('audit', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'libaudit support')
 option('blkid', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'libblkid support')
+option('fdisk', type : 'combo', choices : ['auto', 'true', 'false'],
+       description : 'libfdisk support')
 option('kmod', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'support for loadable modules')
 option('pam', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'PAM support')
+option('pwquality', type : 'combo', choices : ['auto', 'true', 'false'],
+       description : 'libpwquality support')
 option('microhttpd', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'libµhttpd support')
 option('libcryptsetup', type : 'combo', choices : ['auto', 'true', 'false'],
@@ -313,8 +325,6 @@ option('efi-ld', type : 'string',
        description : 'the linker to use for EFI modules')
 option('efi-libdir', type : 'string',
        description : 'path to the EFI lib directory')
-option('efi-ldsdir', type : 'string',
-       description : 'path to the EFI lds directory')
 option('efi-includedir', type : 'string', value : '/usr/include/efi',
        description : 'path to the EFI header directory')
 option('tpm-pcrindex', type : 'integer', value : 8,
index 983f346f1154a9ef01b35ad655c0400d52d7185d..0346a19c82e43cb0154c49d2722ac423f47321cb 100644 (file)
@@ -1,4 +1,5 @@
 src/core/org.freedesktop.systemd1.policy.in
+src/home/org.freedesktop.home1.policy
 src/hostname/org.freedesktop.hostname1.policy
 src/import/org.freedesktop.import1.policy
 src/locale/org.freedesktop.locale1.policy
index c57a4a3cad61226bd919455af14d92b7d5b7f7b1..55d3c82287039a50f41bd56eba223237cfc121ec 100644 (file)
--- a/po/be.po
+++ b/po/be.po
@@ -270,7 +270,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Дазволіць далучаць прылады да працоўных месцаў"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:24
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr "Неабходна аўтэнтыфікацыя для далучэння прылад да працоўных месцаў."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:25
@@ -279,7 +279,7 @@ msgstr "Адключаць прылады ад працоўных месцаў"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:26
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr "Неабходна аўтэнтыфікацыя для адключэння прылад ад працоўных месцаў."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:27
@@ -287,7 +287,7 @@ msgid "Power off the system"
 msgstr "Выключыць сістэму"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:28
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Неабходна аўтэнтыфікацыя для выключэння сістэмы."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:29
@@ -296,20 +296,20 @@ msgstr "Выключыць сістэму пры прысутнасці іншы
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:30
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Неабходна аўтэнтыфікацыя для выключэння сістэмы пры прысутнасці іншых "
 "карыстальнікаў."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:31
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "Выключыць сістэму, калі праграмы перашкаджаюць гэтаму"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:32
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 "Неабходна аўтэнтыфікацыя для выключэння сістэмы, калі праграмы перашкаджаюць "
 "гэтаму."
@@ -319,7 +319,7 @@ msgid "Reboot the system"
 msgstr "Перазагрузіць сістэму"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:34
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Неабходна аўтэнтыфікацыя для перазагрузкі сістэмы."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:35
@@ -328,20 +328,20 @@ msgstr "Перазагрузіць сістэму пры прысутнасці
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:36
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "Неабходна аўтэнтыфікацыя для перазагрузкі сістэмы пры прысутнасці іншых "
 "карыстальнікаў."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:37
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr "Перазагрузіць сістэму, калі праграмы перашкаджаюць гэтаму"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:38
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 "Неабходна аўтэнтыфікацыя для перазагрузкі сістэмы, калі праграмы "
 "перашкаджаюць гэтаму."
@@ -351,7 +351,7 @@ msgid "Suspend the system"
 msgstr "Прыпыніць сістэму"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:40
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Неабходна аўтэнтыфікацыя для прыпынення сістэмы."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:41
@@ -360,20 +360,20 @@ msgstr "Прыпыніць сістэму пры прысутнасці іншы
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:42
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Неабходна аўтэнтыфікацыя для прыпынення сістэмы пры прысутнасці іншых "
 "карыстальнікаў."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:43
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr "Прыпыніць сістэму, калі праграмы перашкаджаюць гэтаму"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:44
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 "Неабходна аўтэнтыфікацыя для прыпынення сістэмы, калі праграмы перашкаджаюць "
 "гэтаму."
@@ -383,7 +383,7 @@ msgid "Hibernate the system"
 msgstr "Гібернаваць сістэму"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:46
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "Неабходна аўтэнтыфікацыя для гібернацыі сістэмы."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:47
@@ -392,20 +392,20 @@ msgstr "Гібернаваць сістэму пры прысутнасці ін
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:48
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Неабходна аўтэнтыфікацыя для гібернацыі сістэмы пры прысутнасці іншых "
 "карыстальнікаў."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:49
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr "Гібернаваць сістэму, калі праграмы перашкаджаюць гэтаму"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:50
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 "Неабходна аўтэнтыфікацыя для гібернацыі сістэмы, калі праграмы перашкаджаюць "
 "гэтаму."
@@ -416,7 +416,7 @@ msgstr "Кіраваць актыўнымі сесіямі, карыстальн
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:52
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Неабходна аўтэнтыфікацыя для кіравання актыўнымі сесіямі, карыстальнікамі і "
 "месцамі."
index eed51937a0f487f5c67fa52d5fde72610b2eecb7..bbd9223b748763bf772671c22f462e4eca486844 100644 (file)
@@ -272,7 +272,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Dazvolić dalučać prylady da pracoŭnych miescaŭ"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:24
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr ""
 "Nieabchodna aŭtentyfikacyja dlia dalučennia prylad da pracoŭnych miescaŭ."
 
@@ -282,7 +282,7 @@ msgstr "Adkliučać prylady ad pracoŭnych miescaŭ"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:26
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Nieabchodna aŭtentyfikacyja dlia adkliučennia prylad ad pracoŭnych miescaŭ."
 
@@ -291,7 +291,7 @@ msgid "Power off the system"
 msgstr "Vykliučyć sistemu"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:28
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Nieabchodna aŭtentyfikacyja dlia vykliučennia sistemy."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:29
@@ -300,20 +300,20 @@ msgstr "Vykliučyć sistemu pry prysutnasci inšych karystaĺnikaŭ"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:30
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Nieabchodna aŭtentyfikacyja dlia vykliučennia sistemy pry prysutnasci inšych "
 "karystaĺnikaŭ."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:31
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "Vykliučyć sistemu, kali prahramy pieraškadžajuć hetamu"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:32
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 "Nieabchodna aŭtentyfikacyja dlia vykliučennia sistemy, kali prahramy "
 "pieraškadžajuć hetamu."
@@ -323,7 +323,7 @@ msgid "Reboot the system"
 msgstr "Pierazahruzić sistemu"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:34
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Nieabchodna aŭtentyfikacyja dlia pierazahruzki sistemy."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:35
@@ -332,20 +332,20 @@ msgstr "Pierazahruzić sistemu pry prysutnasci inšych karystaĺnikaŭ"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:36
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "Nieabchodna aŭtentyfikacyja dlia pierazahruzki sistemy pry prysutnasci "
 "inšych karystaĺnikaŭ."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:37
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr "Pierazahruzić sistemu, kali prahramy pieraškadžajuć hetamu"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:38
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 "Nieabchodna aŭtentyfikacyja dlia pierazahruzki sistemy, kali prahramy "
 "pieraškadžajuć hetamu."
@@ -355,7 +355,7 @@ msgid "Suspend the system"
 msgstr "Prypynić sistemu"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:40
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Nieabchodna aŭtentyfikacyja dlia prypyniennia sistemy."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:41
@@ -364,20 +364,20 @@ msgstr "Prypynić sistemu pry prysutnasci inšych karystaĺnikaŭ"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:42
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Nieabchodna aŭtentyfikacyja dlia prypyniennia sistemy pry prysutnasci inšych "
 "karystaĺnikaŭ."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:43
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr "Prypynić sistemu, kali prahramy pieraškadžajuć hetamu"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:44
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 "Nieabchodna aŭtentyfikacyja dlia prypyniennia sistemy, kali prahramy "
 "pieraškadžajuć hetamu."
@@ -387,7 +387,7 @@ msgid "Hibernate the system"
 msgstr "Hibiernavać sistemu"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:46
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "Nieabchodna aŭtentyfikacyja dlia hibiernacyi sistemy."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:47
@@ -396,20 +396,20 @@ msgstr "Hibiernavać sistemu pry prysutnasci inšych karystaĺnikaŭ"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:48
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Nieabchodna aŭtentyfikacyja dlia hibiernacyi sistemy pry prysutnasci inšych "
 "karystaĺnikaŭ."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:49
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr "Hibiernavać sistemu, kali prahramy pieraškadžajuć hetamu"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:50
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 "Nieabchodna aŭtentyfikacyja dlia hibiernacyi sistemy, kali prahramy "
 "pieraškadžajuć hetamu."
@@ -420,7 +420,7 @@ msgstr "Kiravać aktyŭnymi siesijami, karystaĺnikami i pracoŭnymi miescami"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:52
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Nieabchodna aŭtentyfikacyja dlia kiravannia aktyŭnymi siesijami, "
 "karystaĺnikami i miescami."
index 98edfc5f0262331677a9fecbe46fccc84cc44310..38b952782489768318819a718b5f4cabc990910a 100644 (file)
--- a/po/bg.po
+++ b/po/bg.po
@@ -282,7 +282,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Позволяване на закачане на устройства към работните места"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:24
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr ""
 "За позволяване на закачане на устройства към работните места е необходима "
 "идентификация."
@@ -293,7 +293,7 @@ msgstr "Изчистване на връзките между устройств
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:26
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "За изчистване на връзките между устройствата и работните места е необходима "
 "идентификация."
@@ -303,7 +303,7 @@ msgid "Power off the system"
 msgstr "Изключване на системата"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:28
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "За изключване на системата е необходима идентификация."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:29
@@ -312,20 +312,20 @@ msgstr "Изключване на системата, дори когато им
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:30
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "За изключване на системата, дори когато има други вписани потребители, е "
 "необходима идентификация."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:31
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "Изключване на системата, дори когато програма иска да предотврати това"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:32
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 "За изключване на системата, дори когато програма иска да предотврати това, е "
 "необходима идентификация."
@@ -335,7 +335,7 @@ msgid "Reboot the system"
 msgstr "Рестартиране на системата"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:34
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "За рестартиране на системата е необходима идентификация."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:35
@@ -344,21 +344,21 @@ msgstr "Рестартиране на системата, дори когато
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:36
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "За рестартиране на системата, дори когато има други вписани потребители, е "
 "необходима идентификация."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:37
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr ""
 "Рестартиране на системата, дори когато програма иска да предотврати това"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:38
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 "За рестартиране на системата, дори когато програма иска да предотврати това, "
 "е необходима идентификация."
@@ -368,7 +368,7 @@ msgid "Suspend the system"
 msgstr "Приспиване на системата"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:40
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "За приспиване на системата е необходима идентификация."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:41
@@ -377,20 +377,20 @@ msgstr "Приспиване на системата, дори когато им
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:42
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "За приспиване на системата, дори когато има други вписани потребители, е "
 "необходима идентификация."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:43
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr "Приспиване на системата, дори когато програма иска да предотврати това"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:44
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 "За приспиване на системата, дори когато програма иска да предотврати това, е "
 "необходима идентификация."
@@ -400,7 +400,7 @@ msgid "Hibernate the system"
 msgstr "Дълбоко приспиване на системата"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:46
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "За дълбоко приспиване на системата е необходима идентификация."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:47
@@ -410,22 +410,22 @@ msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:48
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "За дълбоко приспиване на системата, дори когато има други вписани "
 "потребители, е необходима идентификация."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:49
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr ""
 "Дълбоко приспиване на системата, дори когато програма иска да предотврати "
 "това"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:50
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 "За дълбоко приспиване на системата, дори когато програма иска да предотврати "
 "това, е необходима идентификация."
@@ -436,7 +436,7 @@ msgstr "Управление на работещите сесии, потреб
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:52
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "За управление на работещите сесии, потребители и работни места е необходима "
 "идентификация."
index 4a3e221d06767ff5197c8aa217183da4b1a14926..dd8cc56e343b9dd85908e515307ad4f549ffc665 100644 (file)
--- a/po/ca.po
+++ b/po/ca.po
@@ -283,7 +283,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Permet l'annexió de dispositius als llocs de treball"
 
 #: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr ""
 "Es requereix autenticació per annexar un dispositiu a un lloc de treball."
 
@@ -293,7 +293,7 @@ msgstr "Allibera els dispositius a les annexions dels llocs de treball"
 
 #: src/login/org.freedesktop.login1.policy:149
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Es requereix autenticació per restablir com s'annexionen els dispositius als "
 "llocs de treball."
@@ -303,7 +303,7 @@ msgid "Power off the system"
 msgstr "Apaga el sistema"
 
 #: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Es requereix autenticació per apagar el sistema."
 
 #: src/login/org.freedesktop.login1.policy:169
@@ -312,20 +312,20 @@ msgstr "Apaga el sistema mentre hi ha altres usuaris amb la sessió iniciada"
 
 #: src/login/org.freedesktop.login1.policy:170
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Es requereix autenticació per apagar el sistema mentre hi ha altres usuaris "
 "amb la sessió iniciada."
 
 #: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "Apaga el sistema mentre hi ha una aplicació que ha demanat inhibir-ho"
 
 #: src/login/org.freedesktop.login1.policy:181
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 "Es requereix autenticació per apagar el sistema mentre hi ha una aplicació "
 "que ha demanat inhibir-ho."
@@ -335,7 +335,7 @@ msgid "Reboot the system"
 msgstr "Reinicia el sistema"
 
 #: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Es requereix autenticació per reiniciar el sistema."
 
 #: src/login/org.freedesktop.login1.policy:202
@@ -344,21 +344,21 @@ msgstr "Reinicia el sistema mentre hi ha usuaris amb la sessió iniciada"
 
 #: src/login/org.freedesktop.login1.policy:203
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "Es requereix autenticació per reiniciar el sistema mentre hi ha usuaris amb "
 "la sessió iniciada."
 
 #: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr ""
 "Reinicia el sistema mentre hi ha una aplicació que ha demanat inhibir-ho"
 
 #: src/login/org.freedesktop.login1.policy:214
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 "Es requereix autenticació per reiniciar el sistema mentre hi ha una "
 "aplicació que ha demanat inhibir-ho."
@@ -368,7 +368,7 @@ msgid "Halt the system"
 msgstr "Atura el sistema"
 
 #: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
 msgstr "Es requereix autenticació per aturar el sistema."
 
 #: src/login/org.freedesktop.login1.policy:235
@@ -377,20 +377,20 @@ msgstr "Atura el sistema mentre hi ha altres usuaris amb la sessió iniciada"
 
 #: src/login/org.freedesktop.login1.policy:236
 msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt the system while other users are "
 "logged in."
 msgstr ""
 "Es requereix autenticació per aturar el sistema mentre hi ha altres usuaris "
 "amb la sessió iniciada."
 
 #: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
 msgstr ""
 "Atura el sistema mentre hi ha una aplicació que ha demanat d'inhibir-ho"
 
 #: src/login/org.freedesktop.login1.policy:247
 msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
 "to inhibit it."
 msgstr ""
 "Es requereix autenticació per aturar el sistema mentre hi ha una aplicació "
@@ -401,7 +401,7 @@ msgid "Suspend the system"
 msgstr "Suspèn el sistema"
 
 #: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Es requereix autenticació per suspendre el sistema."
 
 #: src/login/org.freedesktop.login1.policy:267
@@ -410,21 +410,21 @@ msgstr "Suspèn el sistema mentre hi ha altres usuaris amb la sessió iniciada"
 
 #: src/login/org.freedesktop.login1.policy:268
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Es requereix autenticació per reiniciar el sistema mentre hi ha altres "
 "usuaris amb la sessió iniciada."
 
 #: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr ""
 "Suspèn el sistema mentre hi ha una aplicació que ha demanat d'inhibir-ho"
 
 #: src/login/org.freedesktop.login1.policy:279
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 "Es requereix autenticació per suspendre el sistema mentre hi ha una "
 "aplicació que ha demanat d'inhibir-ho."
@@ -434,7 +434,7 @@ msgid "Hibernate the system"
 msgstr "Hiberna el sistema"
 
 #: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "Es requereix autenticació per hibernar el sistema."
 
 #: src/login/org.freedesktop.login1.policy:299
@@ -443,21 +443,21 @@ msgstr "Hiberna el sistema mentre hi ha altres usuaris amb la sessió iniciada"
 
 #: src/login/org.freedesktop.login1.policy:300
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Es requereix autenticació per hibernar el sistema mentre hi ha altres "
 "usuaris amb la sessió iniciada."
 
 #: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr ""
 "Hiberna el sistema mentre hi ha una aplicació que ha demanat d'inhibir-ho"
 
 #: src/login/org.freedesktop.login1.policy:311
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 "Es requereix autenticació per hibernar el sistema mentre hi ha una aplicació "
 "que ha demanat d'inhibir-ho."
@@ -468,7 +468,7 @@ msgstr "Gestiona les sessions, usuaris i llocs de treball actius"
 
 #: src/login/org.freedesktop.login1.policy:322
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Es requereix autenticació per gestionar les sessions, usuaris i llocs de "
 "treball actius."
index 985be305eaa70b43dc6cd578fe614f222e57bd72..819800132a4d30af65d9df283fa748dabee66316 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: 2019-09-17 14:31+0000\n"
-"PO-Revision-Date: 2019-09-19 15:48+0200\n"
+"POT-Creation-Date: 2020-02-29 15:12+0000\n"
+"PO-Revision-Date: 2020-03-01 13:58+0100\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.2.3\n"
+"X-Generator: Poedit 2.3\n"
 
 #: src/core/org.freedesktop.systemd1.policy.in:22
 msgid "Send passphrase back to system"
@@ -64,7 +64,60 @@ msgstr "Znovu načíst stav systemd"
 
 #: src/core/org.freedesktop.systemd1.policy.in:65
 msgid "Authentication is required to reload the systemd state."
-msgstr "Pro znovu načtení stavu systemd je vyžadováno ověření."
+msgstr "Pro opětovné načtení stavu systemd je vyžadováno ověření."
+
+#: src/home/org.freedesktop.home1.policy:13
+msgid "Create a home area"
+msgstr "Vytvořit domovský adresář"
+
+#: src/home/org.freedesktop.home1.policy:14
+msgid "Authentication is required to create a user's home area."
+msgstr "Pro vytvoření domovského adresáře uživatele je vyžadováno ověření."
+
+#: src/home/org.freedesktop.home1.policy:23
+msgid "Remove a home area"
+msgstr "Odebrat domovský adresář"
+
+#: src/home/org.freedesktop.home1.policy:24
+msgid "Authentication is required to remove a user's home area."
+msgstr "Pro odebrání domovského adresáře uživatele je vyžadováno ověření."
+
+#: src/home/org.freedesktop.home1.policy:33
+msgid "Check credentials of a home area"
+msgstr "Zkontrolovat pověření domovského adresáře"
+
+#: src/home/org.freedesktop.home1.policy:34
+msgid ""
+"Authentication is required to check credentials against a user's home area."
+msgstr ""
+"Pro kontrolu pověření proti domovskému adresáři uživatele je vyžadováno "
+"ověření."
+
+#: src/home/org.freedesktop.home1.policy:43
+msgid "Update a home area"
+msgstr "Aktualizovat domovský adresář"
+
+#: src/home/org.freedesktop.home1.policy:44
+msgid "Authentication is required to update a user's home area."
+msgstr "Pro aktualizaci domovského adresáře uživatele je vyžadováno ověření."
+
+#: src/home/org.freedesktop.home1.policy:53
+msgid "Resize a home area"
+msgstr "Změnit velikost domovského adresáře"
+
+#: src/home/org.freedesktop.home1.policy:54
+msgid "Authentication is required to resize a user's home area."
+msgstr ""
+"Pro změnu velikosti domovského adresáře uživatele je vyžadováno ověření."
+
+#: src/home/org.freedesktop.home1.policy:63
+msgid "Change password of a home area"
+msgstr "Změnit heslo domovského adresáře"
+
+#: src/home/org.freedesktop.home1.policy:64
+msgid ""
+"Authentication is required to change the password of a user's home area."
+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"
@@ -263,7 +316,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Povolit připojování zařízení ke stanovištím"
 
 #: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr "Pro připojování zařízení ke stanovišti je vyžadováno ověření."
 
 #: src/login/org.freedesktop.login1.policy:148
@@ -271,18 +324,17 @@ msgid "Flush device to seat attachments"
 msgstr "Odstranit přiřazení zařízení ke stanovištím"
 
 #: src/login/org.freedesktop.login1.policy:149
-msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+msgid "Authentication is required to reset how devices are attached to seats."
 msgstr ""
-"Pro reset způsobu jak jsou zařízení přiřazována ke stanovištím je vyžadováno "
-"ověření."
+"Pro resetování způsobu jak jsou zařízení přiřazována ke stanovištím je "
+"vyžadováno ověření."
 
 #: src/login/org.freedesktop.login1.policy:158
 msgid "Power off the system"
 msgstr "Vypnout systém"
 
 #: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Pro vypnutí systému je vyžadováno ověření."
 
 #: src/login/org.freedesktop.login1.policy:169
@@ -291,20 +343,20 @@ msgstr "Vypnout systém, i když jsou přihlášeni další uživatelé"
 
 #: src/login/org.freedesktop.login1.policy:170
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Pro vypnutí systému, když jsou přihlášeni další uživatelé je vyžadováno "
 "ověření."
 
 #: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "Vypnout systém, i když aplikace požádala o zákaz vypnutí"
 
 #: src/login/org.freedesktop.login1.policy:181
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application is "
+"inhibiting this."
 msgstr ""
 "Pro vypnutí systému, když aplikace požádala o zákaz vypnutí je vyžadováno "
 "ověření."
@@ -314,7 +366,7 @@ msgid "Reboot the system"
 msgstr "Restartovat systém"
 
 #: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Pro restartování systému je vyžadováno ověření."
 
 #: src/login/org.freedesktop.login1.policy:202
@@ -323,20 +375,20 @@ msgstr "Restartovat systém, i když jsou přihlášeni další uživatelé"
 
 #: src/login/org.freedesktop.login1.policy:203
 msgid ""
-"Authentication is required for rebooting the system while other users are "
-"logged in."
+"Authentication is required to reboot the system while other users are logged "
+"in."
 msgstr ""
 "Pro restartování systému, když jsou přihlášeni další uživatelé je vyžadováno "
 "ověření."
 
 #: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr "Restartovat systém, i když aplikace požádala o zákaz restartu"
 
 #: src/login/org.freedesktop.login1.policy:214
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application is "
+"inhibiting this."
 msgstr ""
 "Pro restartování systému, když aplikace požádala o zákaz restartu je "
 "vyžadováno ověření."
@@ -346,7 +398,7 @@ msgid "Halt the system"
 msgstr "Zastavit systém"
 
 #: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
 msgstr "Pro zastavení systému je vyžadováno ověření."
 
 #: src/login/org.freedesktop.login1.policy:235
@@ -355,20 +407,20 @@ msgstr "Zastavit systém, i když jsou přihlášeni další uživatelé"
 
 #: src/login/org.freedesktop.login1.policy:236
 msgid ""
-"Authentication is required for halting the system while other users are "
-"logged in."
+"Authentication is required to halt the system while other users are logged "
+"in."
 msgstr ""
 "Pro zastavení systému, když jsou přihlášeni další uživatelé je vyžadováno "
 "ověření."
 
 #: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
 msgstr "Zastavit systém, i když aplikace požádala o zákaz zastavení"
 
 #: src/login/org.freedesktop.login1.policy:247
 msgid ""
-"Authentication is required for halting the system while an application asked "
-"to inhibit it."
+"Authentication is required to halt the system while an application is "
+"inhibiting this."
 msgstr ""
 "Pro zastavení systému, když aplikace požádala o zákaz zastavení je "
 "vyžadováno ověření."
@@ -378,7 +430,7 @@ msgid "Suspend the system"
 msgstr "Uspat systém"
 
 #: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Pro uspání systému je vyžadováno ověření."
 
 #: src/login/org.freedesktop.login1.policy:267
@@ -387,20 +439,20 @@ msgstr "Uspat systém, i když jsou přihlášeni další uživatelé"
 
 #: src/login/org.freedesktop.login1.policy:268
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Pro uspání systému, když jsou přihlášeni další uživatelé je vyžadováno "
 "ověření."
 
 #: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr "Uspat systém, i když aplikace požádala o zákaz uspání"
 
 #: src/login/org.freedesktop.login1.policy:279
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application is "
+"inhibiting this."
 msgstr ""
 "Pro uspání systému, když aplikace požádala o zákaz uspání je vyžadováno "
 "ověření."
@@ -410,7 +462,7 @@ msgid "Hibernate the system"
 msgstr "Hibernovat systém"
 
 #: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "Pro hibernaci systému je vyžadováno ověření."
 
 #: src/login/org.freedesktop.login1.policy:299
@@ -419,20 +471,20 @@ msgstr "Hibernovat systém, i když jsou přihlášeni další uživatelé"
 
 #: src/login/org.freedesktop.login1.policy:300
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Pro hibernaci systému, když jsou přihlášeni další uživatelé je vyžadováno "
 "ověření."
 
 #: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr "Hibernovat systém, i když aplikace požádala o zákaz hibernace"
 
 #: src/login/org.freedesktop.login1.policy:311
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application is "
+"inhibiting this."
 msgstr ""
 "Pro hibernaci systému, když aplikace požádala o zákaz hibernace je "
 "vyžadováno ověření."
@@ -442,8 +494,7 @@ msgid "Manage active sessions, users and seats"
 msgstr "Spravovat aktivní sezení, uživatele a stanoviště"
 
 #: src/login/org.freedesktop.login1.policy:322
-msgid ""
-"Authentication is required for managing active sessions, users and seats."
+msgid "Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Pro správu aktivních sezení, uživatelů a stanovišť je vyžadováno ověření."
 
@@ -505,6 +556,14 @@ msgstr "Nastavit zprávu všem uživatelům"
 msgid "Authentication is required to set a wall message"
 msgstr "K nastavení zprávy všem uživatelům je vyžadováno ověření"
 
+#: src/login/org.freedesktop.login1.policy:395
+msgid "Change Session"
+msgstr "Změnit sezení"
+
+#: src/login/org.freedesktop.login1.policy:396
+msgid "Authentication is required to change the virtual terminal."
+msgstr "Pro změnu virtuálního terminálu je vyžadováno ověření."
+
 #: src/machine/org.freedesktop.machine1.policy:22
 msgid "Log into a local container"
 msgstr "Přihlásit se do lokálního kontejneru"
@@ -680,6 +739,30 @@ msgstr "Vrátit změny nastavení DNS"
 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 "Renew dynamic addresses"
+msgstr "Obnovit dynamické adresy"
+
+#: src/network/org.freedesktop.network1.policy:144
+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
+msgid "Reload network settings"
+msgstr "Znovu načíst nastavení sítě"
+
+#: src/network/org.freedesktop.network1.policy:155
+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
+msgid "Reconfigure network interface"
+msgstr "Přenastavit síťové rozhraní"
+
+#: src/network/org.freedesktop.network1.policy:166
+msgid "Authentication is required to reconfigure network interface."
+msgstr "Pro přenastavení síťového rozhraní je vyžadováno ověření."
+
 #: src/portable/org.freedesktop.portable1.policy:13
 msgid "Inspect a portable service image"
 msgstr "Prohlédnout obraz přenosné služby"
@@ -770,43 +853,40 @@ msgid ""
 "shall be enabled."
 msgstr "Pro kontrolu synchronizace času ze sítě je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:354
+#: src/core/dbus-unit.c:356
 msgid "Authentication is required to start '$(unit)'."
 msgstr "Pro spuštění „$(unit)” je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:355
+#: src/core/dbus-unit.c:357
 msgid "Authentication is required to stop '$(unit)'."
 msgstr "Pro vypnutí „$(unit)” je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:356
+#: src/core/dbus-unit.c:358
 msgid "Authentication is required to reload '$(unit)'."
-msgstr "Pro znovu načtení „$(unit)” je vyžadováno ověření."
+msgstr "Pro opětovné načtení „$(unit)” je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:357 src/core/dbus-unit.c:358
+#: src/core/dbus-unit.c:359 src/core/dbus-unit.c:360
 msgid "Authentication is required to restart '$(unit)'."
 msgstr "Pro restart „$(unit)” je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:530
+#: src/core/dbus-unit.c:532
 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:561
+#: src/core/dbus-unit.c:563
 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:594
+#: src/core/dbus-unit.c:596
 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:703
+#: src/core/dbus-unit.c:705
 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í."
-
-#~ msgid "Authentication is required to kill '$(unit)'."
-#~ msgstr "Pro ukončení „$(unit)” je vyžadováno ověření."
index cc2344712984f389ca77477707b6b46597a0d721..7dee612867656dad3a9cbe49187c8e550a0a3d9e 100644 (file)
--- a/po/da.po
+++ b/po/da.po
@@ -256,7 +256,7 @@ msgstr "Tillad at montere af enheder til arbejdsstationer"
 
 # www.freedesktop.org/wiki/Software/systemd/multiseat/
 #: ../src/login/org.freedesktop.login1.policy.in.h:22
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr ""
 "Autentificering er nødvendig for at montere en enhed til en "
 "arbejdsstation."
@@ -269,7 +269,7 @@ msgstr "Nulstil enhed monteret til en arbejdsstation"
 # www.freedesktop.org/wiki/Software/systemd/multiseat/
 #: ../src/login/org.freedesktop.login1.policy.in.h:24
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Autentificering er nødvendig for at nulstille måden enheder er monteret "
 "arbejdsstationer."
@@ -279,7 +279,7 @@ msgid "Power off the system"
 msgstr "Sluk for systemet"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:26
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Autentificering er nødvendig for at slukke systemet"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:27
@@ -288,21 +288,21 @@ msgstr "Sluk systemet mens andre brugere er logget på"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:28
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Autentificering er nødvendig for at slukke systemet mens andre brugere "
 "er logget på."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:29
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr ""
 "Sluk for systemet mens en applikation har forespurgt at hæmme det"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:30
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 "Autentificering er nødvendig for at slukke systemet mens en applikation har "
 "forespurgt at hæmme det."
@@ -312,7 +312,7 @@ msgid "Reboot the system"
 msgstr "Genstart systemet"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:32
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Autentificering er nødvendig for at genstarte systemet."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:33
@@ -321,21 +321,21 @@ msgstr "Genstart systemet mens andre brugere er logget ind"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:34
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "Autentificering er nødvendig for at genstarte systemet mens andre brugere "
 "er logget ind."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:35
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr ""
 "Genstart systemet mens en applikation har forespurgt at hæmme det"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:36
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 "Autentificering er nødvendig for at genstarte systemet mens en applikation "
 "har forespurgt at hæmme det."
@@ -345,7 +345,7 @@ msgid "Suspend the system"
 msgstr "Sæt systemet på standby"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:38
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Autentificering er nødvendig for at sætte systemet på standby"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:39
@@ -354,22 +354,22 @@ msgstr "Sæt systemet på standby mens andre brugere er logget på"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:40
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Autentificering er nødvendig for at sætte systemet på standby, mens andre "
 "brugere er logget på."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:41
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr ""
 "Sæt systemet på standby mens en applikation har forespurgt at hæmme"
 "det"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:42
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 "Autentificering er nødvendig for at sætte systemet på standby, mens en "
 "applikation har forespurgt at hæmme det."
@@ -379,7 +379,7 @@ msgid "Hibernate the system"
 msgstr "Sæt systemet i dvale"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:44
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr ""
 "Autentificering er nødvendig for at sætte systemet i dvale-tilstand."
 
@@ -390,21 +390,21 @@ msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:46
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Autentificering er nødvendig for at sætte systemet i dvale-tilstand, mens "
 "andre brugere er logget på."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:47
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr "Sæt systemet i dvale-tilstand mens en applikation har forespurgt at "
 "hæmme det"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:48
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 "Autentificering er nødvendig for at sætte systemet i dvale tilstand, mens "
 "en applikation har forespurgt at hæmme det."
@@ -416,7 +416,7 @@ msgstr "Håndtér aktive sessioner, brugere og arbejdsstationer"
 # www.freedesktop.org/wiki/Software/systemd/multiseat/
 #: ../src/login/org.freedesktop.login1.policy.in.h:50
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Autentificering er nødvendig for at håndtere aktive sessioner, brugere "
 "og arbejdsstationer."
index eb3423c24a19c820fb45bd4244b5d2b4846eb583..cca79207d417bf5a4c903d7b776599eb0c5217b9 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -274,7 +274,7 @@ msgstr "Das Anschließen von Geräten an Arbeitsstationen erlauben"
 
 # www.freedesktop.org/wiki/Software/systemd/multiseat/
 #: ../src/login/org.freedesktop.login1.policy.in.h:22
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr ""
 "Legitimierung ist zum Anschließen eines Geräts an eine Arbeitsstation "
 "notwendig."
@@ -287,7 +287,7 @@ msgstr "Zurücksetzen der an eine Arbeitsstation angeschlossenen Geräte"
 # www.freedesktop.org/wiki/Software/systemd/multiseat/
 #: ../src/login/org.freedesktop.login1.policy.in.h:24
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Legitimierung ist zum Zurücksetzen notwendig, wie Geräte an eine "
 "Arbeitsstation angeschlossen werden."
@@ -297,7 +297,7 @@ msgid "Power off the system"
 msgstr "Das System ausschalten"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:26
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Legitimierung ist zum Ausschalten des Systems notwendig."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:27
@@ -306,21 +306,21 @@ msgstr "Das System herunter fahren, während andere Benutzer angemeldet sind"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:28
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Legitimierung ist zum Herunterfahren des Systems notwendig, während andere "
 "Benutzer angemeldet sind."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:29
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr ""
 "Das System ausschalten, während eine Anwendung anfordert es zu unterbinden"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:30
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 "Legitimierung ist zum Ausschalten des Systems notwendig, während eine "
 "Anwendung anfordert es zu unterbinden."
@@ -330,7 +330,7 @@ msgid "Reboot the system"
 msgstr "Das System neu starten"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:32
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Legitimierung ist zum Neustart des Systems notwendig."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:33
@@ -339,21 +339,21 @@ msgstr "Das Systems neu starten, während andere Benutzer angemeldet sind"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:34
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "Legitimierung ist zum Neustart des Systems notwendig, während andere "
 "Benutzer angemeldet sind."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:35
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr ""
 "Das System neu starten, während eine Anwendung anfordert es zu unterbinden"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:36
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 "Legitimierung ist zum Neustart des Systems notwendig, während eine Anwendung "
 "anforderte es zu unterbinden."
@@ -363,7 +363,7 @@ msgid "Suspend the system"
 msgstr "Das System in Bereitschaft versetzen"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:38
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Legitimierung ist zum Versetzen des Systems in Bereitschaft notwendig."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:39
@@ -374,22 +374,22 @@ msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:40
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Legitimierung ist zum Versetzen des Systems in Bereitschaft notwendig, "
 "während andere Benutzer angemeldet sind."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:41
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr ""
 "Das System in Bereitschaft versetzen, während eine Anwendung anfordert dies "
 "zu unterbinden"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:42
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 "Legitimierung ist zum Versetzen des Systems in Bereitschaft notwendig, "
 "während eine Anwendung anfordert dies zu unterbinden."
@@ -399,7 +399,7 @@ msgid "Hibernate the system"
 msgstr "Den Ruhezustand des Systems aktivieren"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:44
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr ""
 "Legitimierung ist zum Aktivieren des Ruhezustands des Systems notwendig."
 
@@ -411,22 +411,22 @@ msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:46
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Legitimierung ist zum Aktivieren des Ruhezustands des Systems notwendig, "
 "während andere Benutzer angemeldet sind."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:47
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr ""
 "Das System in den Ruhezustand versetzen, während eine Anwendung wünscht dies "
 "zu verhindern"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:48
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 "Legitimierung ist zum Versetzen des System in den Ruhezustand notwendig, "
 "während eine Anwendung wünscht dies zu verhindern."
@@ -438,7 +438,7 @@ msgstr "Aktive Sitzungen, Benutzer und Arbeitsstationen verwalten"
 # www.freedesktop.org/wiki/Software/systemd/multiseat/
 #: ../src/login/org.freedesktop.login1.policy.in.h:50
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Legitimierung ist zur Verwaltung aktiver Sitzungen, Benutzern und "
 "Arbeitsstationen notwendig."
index 271a5d42031d3f2329a81f454f65db4d89a42caa..d96bf16264fa1d48df724c3171a12e0fc06cba75 100644 (file)
--- a/po/el.po
+++ b/po/el.po
@@ -277,7 +277,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Να επιτρέπεται η προσάρτηση συσκευών στους σταθμούς εργασίας"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:22
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr ""
 "Απαιτείται πιστοποίηση για προσάρτηση μιας συσκευής σε έναν σταθμό εργασίας."
 
@@ -287,7 +287,7 @@ msgstr "Αφαίρεση συσκευής από προσαρτήσεις στα
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:24
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Απαιτείται πιστοποίηση για επαναφορά του τρόπου που οι συσκευές προσαρτώνται "
 "στους σταθμούς εργασίας."
@@ -297,7 +297,7 @@ msgid "Power off the system"
 msgstr "Σβήσιμο του συστήματος"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:26
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Απαιτείται πιστοποίηση για την σβήσιμο του συστήματος."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:27
@@ -306,20 +306,20 @@ msgstr "Σβήσιμο του συστήματος ενώ άλλοι χρήστ
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:28
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Απαιτείται πιστοποίηση για σβήσιμο του συστήματος ενώ άλλοι χρήστες είναι "
 "συνδεμένοι."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:29
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "Απενεργοποίηση του συστήματος ενώ μια εφαρμογή ζήτησε να αποτραπεί."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:30
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 "Απαιτείται πιστοποίηση για απενεργοποίηση του συστήματος ενώ μια εφαρμογή "
 "ζήτησε να αποτραπεί."
@@ -329,7 +329,7 @@ msgid "Reboot the system"
 msgstr "Επανεκκίνηση του συστήματος"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:32
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Απαιτείται πιστοποίηση για επανεκκίνηση του συστήματος."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:33
@@ -338,20 +338,20 @@ msgstr "Επανεκκίνηση του συστήματος ενώ άλλοι 
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:34
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "Απαιτείται πιστοποίηση για επανεκκίνηση του συστήματος ενώ άλλοι χρήστες "
 "είναι συνδεμένοι."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:35
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr "Επανεκκίνηση του συστήματος ενώ μια εφαρμογή ζήτησε να αποτραπεί"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:36
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 "Απαιτείται πιστοποίηση για επανεκκίνηση του συστήματος ενώ μια εφαρμογή "
 "ζήτησε να αποτραπεί."
@@ -361,7 +361,7 @@ msgid "Suspend the system"
 msgstr "Αναστολή του συστήματος"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:38
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Απαιτείται πιστοποίηση για την αναστολή του συστήματος."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:39
@@ -370,20 +370,20 @@ msgstr "Αναστολή του συστήματος ενώ άλλοι χρήσ
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:40
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Απαιτείται πιστοποίηση για αναστολή του συστήματος ενώ άλλοι χρήστες είναι "
 "συνδεμένοι."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:41
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr "Αναστολή του συστήματος ενώ μια εφαρμογή ζήτησε να αποτραπεί"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:42
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 "Απαιτείται πιστοποίηση για αναστολή του συστήματος ενώ μια εφαρμογή ζήτησε "
 "να αποτραπεί."
@@ -393,7 +393,7 @@ msgid "Hibernate the system"
 msgstr "Αδρανοποίηση του συτήματος"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:44
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "Απαιτείται πιστοποίηση για αδρανοποίηση του συστήματος."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:45
@@ -402,20 +402,20 @@ msgstr "Αδρανοποίηση του συστήματος ενώ άλλοι 
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:46
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Απαιτείται πιστοποίηση για αδρανοποίηση του συστήματος ενώ άλλοι χρήστες "
 "είναι συνδεμένοι."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:47
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr "Αδρανοποίηση του συστήματος ενώ μια εφαρμογή ζήτησε να αποτραπεί"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:48
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 "Απαιτείται πιστοποίηση για αδρανοποίηση του συστήματος ενώ μια εφαρμογή "
 "ζήτησε να αποτραπεί."
@@ -427,7 +427,7 @@ msgstr ""
 #: ../src/login/org.freedesktop.login1.policy.in.h:50
 #, fuzzy
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Απαιτείται πιστοποίηση για προσάρτηση μιας συσκευής σε έναν σταθμό εργασίας."
 
index 786e0d2ce726ddee9c97155d4441e616df5e2c30..aa586a729d9998b2a5fa5106a4066b7a5bb61387 100644 (file)
--- a/po/es.po
+++ b/po/es.po
@@ -270,7 +270,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Permitir la conexión de dispositivos a los puestos de trabajo"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:22
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr ""
 "Se requiere autenticación para conectar un dispositivo a un puesto de "
 "trabajo."
@@ -281,7 +281,7 @@ msgstr "Refrescar los dispositivos asociados a cada puesto de trabajo"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:24
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Se requiere autenticación para reconfigurar los dispositivos asociados a "
 "cada puesto de trabajo."
@@ -291,7 +291,7 @@ msgid "Power off the system"
 msgstr "Apagar el sistema"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:26
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Se requiere autenticación para apagar el sistema."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:27
@@ -300,20 +300,20 @@ msgstr "Apagar el sistema mientras todavía hay usuarios conectados"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:28
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Se requiere autenticación para apagar el sistema mientras todavía hay "
 "usuarios conectados."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:29
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "Apagar el sistema a pesar de que una aplicación lo impide"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:30
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 "Se requiere autenticación para apagar el sistema a pesar de que una "
 "aplicación lo impide."
@@ -323,7 +323,7 @@ msgid "Reboot the system"
 msgstr "Reiniciar el sistema"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:32
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Se requiere autenticación para reiniciar el sistema."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:33
@@ -332,20 +332,20 @@ msgstr "Reiniciar el sistema mientras todavía hay usuarios conectados"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:34
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "Se requiere autenticación para reiniciar el sistema mientras todavía hay "
 "usuarios conectados."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:35
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr "Reiniciar el sistema a pesar de que una aplicación lo impide"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:36
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 "Se requiere autenticación para reiniciar el sistema a pesar de que una "
 "aplicación lo impide."
@@ -355,7 +355,7 @@ msgid "Suspend the system"
 msgstr "Suspender el sistema"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:38
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Se requiere autenticación para suspender el sistema."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:39
@@ -364,20 +364,20 @@ msgstr "Suspender el sistema mientras todavía hay usuarios conectados"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:40
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Se requiere autenticación para suspender el sistema mientras todavía hay "
 "usuarios conectados."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:41
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr "Suspender el sistema a pesar de que una aplicación lo impide"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:42
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 "Se requiere autenticación para suspender el sistema a pesar de que una "
 "aplicación lo impide."
@@ -387,7 +387,7 @@ msgid "Hibernate the system"
 msgstr "Hibernar el sistema"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:44
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "Se requiere autenticación para hibernar el sistema."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:45
@@ -396,20 +396,20 @@ msgstr "Hibernar el sistema mientras todavía hay usuarios conectados"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:46
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Se requiere autenticación para hibernar el sistema mientras todavía hay "
 "usuarios conectados."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:47
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr "Hibernar el sistema a pesar de que una aplicación lo impide"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:48
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 "Se requiere autenticación para hibernar el sistema a pesar de que una "
 "aplicación lo impide."
@@ -420,7 +420,7 @@ msgstr "Administrar sesiones activas, usuarios y puestos de trabajo"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:50
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Se requiere autenticación para administrar las sesiones activas, usuarios y "
 "puestos de trabajo."
index 3f0997885069ff5b6e57f7808d62e6e3eca65d16..0c5e0530184a0e7e8fa2e58f8eecd93494e326db 100644 (file)
--- a/po/fr.po
+++ b/po/fr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: systemd\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-03-07 22:43+0100\n"
+"POT-Creation-Date: 2020-02-03 01:21+0100\n"
 "PO-Revision-Date: 2019-03-07 23:09+0100\n"
 "Last-Translator: Sylvain Plantefève <sylvain.plantefeve@gmail.com>\n"
 "Language-Team: French\n"
@@ -67,6 +67,63 @@ msgstr "Recharger l'état de systemd"
 msgid "Authentication is required to reload the systemd state."
 msgstr "Authentification requise pour recharger l'état de systemd"
 
+#: src/home/org.freedesktop.home1.policy:13
+msgid "Create a home area"
+msgstr "Créer un espace personnel"
+
+#: src/home/org.freedesktop.home1.policy:14
+msgid "Authentication is required to create a user's home area."
+msgstr ""
+"Authentification requise pour créer l'espace personnel d'un utilisateur."
+
+#: src/home/org.freedesktop.home1.policy:23
+msgid "Remove a home area"
+msgstr "Retirer un espace personnel"
+
+#: src/home/org.freedesktop.home1.policy:24
+msgid "Authentication is required to remove a user's home area."
+msgstr ""
+"Authentification requise pour retirer l'espace personnel d'un utilisateur."
+
+#: src/home/org.freedesktop.home1.policy:33
+msgid "Check credentials of a home area"
+msgstr "Vérifier les identifiants d'un espace personnel"
+
+#: src/home/org.freedesktop.home1.policy:34
+msgid ""
+"Authentication is required to check credentials against a user's home area."
+msgstr ""
+"Authentification requise pour vérifier les identifiants de l'espace "
+"personnel d'un utilisateur."
+
+#: src/home/org.freedesktop.home1.policy:43
+msgid "Update a home area"
+msgstr "Mettre à jour un espace personnel"
+
+#: src/home/org.freedesktop.home1.policy:44
+msgid "Authentication is required to update a user's home area."
+msgstr ""
+"Authentification requise pour mettre à jour l'espace personnel d'un "
+"utilisateur."
+
+#: src/home/org.freedesktop.home1.policy:53
+msgid "Resize a home area"
+msgstr "Retailler un espace personnel"
+
+#: src/home/org.freedesktop.home1.policy:54
+msgid "Authentication is required to resize a user's home area."
+msgstr "Authentification requise pour retailler un espace personnel."
+
+#: src/home/org.freedesktop.home1.policy:63
+msgid "Change password of a home area"
+msgstr "Changer le mot de passe d'un espace personnel"
+
+#: src/home/org.freedesktop.home1.policy:64
+msgid "Authentication is required to change the password of a user's home area."
+msgstr ""
+"Authentification requise pour changer le mot de passe de l'espace personnel "
+"d'un utilisateur."
+
 #: src/hostname/org.freedesktop.hostname1.policy:20
 msgid "Set host name"
 msgstr "Définir le nom d'hôte"
@@ -286,7 +343,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Permet d'associer des périphériques à des postes (seats)"
 
 #: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr ""
 "Authentification requise pour associer un périphérique à un poste (seat)."
 
@@ -296,7 +353,7 @@ msgstr "Révoquer les associations de périphériques aux postes (seats)"
 
 #: src/login/org.freedesktop.login1.policy:149
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Authentification requise pour révoquer les associations de périphériques aux "
 "postes (seats)."
@@ -306,7 +363,7 @@ msgid "Power off the system"
 msgstr "Éteindre le système"
 
 #: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Authentification requise pour éteindre le système."
 
 #: src/login/org.freedesktop.login1.policy:169
@@ -315,20 +372,20 @@ msgstr "Éteindre le système alors que d'autres utilisateurs sont connectés"
 
 #: src/login/org.freedesktop.login1.policy:170
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Authentification requise pour éteindre le système alors que d'autres "
 "utilisateurs sont connectés."
 
 #: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "Éteindre le système alors qu'une application a demandé de l'empêcher"
 
 #: src/login/org.freedesktop.login1.policy:181
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 "Authentification requise pour éteindre le système alors qu'une application a "
 "demandé de l'empêcher."
@@ -338,7 +395,7 @@ msgid "Reboot the system"
 msgstr "Redémarrer le système"
 
 #: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Authentification requise pour redémarrer le système."
 
 #: src/login/org.freedesktop.login1.policy:202
@@ -347,20 +404,20 @@ msgstr "Redémarrer le système alors que d'autres utilisateurs sont connectés"
 
 #: src/login/org.freedesktop.login1.policy:203
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "Authentification requise pour redémarrer le système alors que d'autres "
 "utilisateurs sont connectés."
 
 #: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr "Redémarrer le système alors qu'une application a demandé de l'empêcher"
 
 #: src/login/org.freedesktop.login1.policy:214
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 "Authentification requise pour redémarrer le système alors qu'une application "
 "a demandé de l'empêcher."
@@ -370,7 +427,7 @@ msgid "Halt the system"
 msgstr "Arrêter le système"
 
 #: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
 msgstr "Authentification requise pour arrêter le système."
 
 #: src/login/org.freedesktop.login1.policy:235
@@ -379,19 +436,19 @@ msgstr "Arrêter le système alors que d'autres utilisateurs sont connectés"
 
 #: src/login/org.freedesktop.login1.policy:236
 msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt the system while other users are "
 "logged in."
 msgstr ""
 "Authentification requise pour arrêter le système alors que d'autres "
 "utilisateurs sont connectés."
 
 #: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
 msgstr "Arrêter le système alors qu'une application a demandé de l'empêcher"
 
 #: src/login/org.freedesktop.login1.policy:247
 msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
 "to inhibit it."
 msgstr ""
 "Authentification requise pour arrêter le système alors qu'une application a "
@@ -402,7 +459,7 @@ msgid "Suspend the system"
 msgstr "Mettre le système en veille"
 
 #: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Authentification requise pour mettre le système en veille."
 
 #: src/login/org.freedesktop.login1.policy:267
@@ -412,21 +469,21 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:268
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Authentification requise pour mettre le système en veille alors que d'autres "
 "utilisateurs sont connectés."
 
 #: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr ""
 "Mettre le système en veille alors qu'une application a demandé de l'empêcher"
 
 #: src/login/org.freedesktop.login1.policy:279
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 "Authentification requise pour mettre le système en veille alors qu'une "
 "application a demandé de l'empêcher."
@@ -436,7 +493,7 @@ msgid "Hibernate the system"
 msgstr "Mettre le système en hibernation"
 
 #: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "Authentification requise pour mettre le système en hibernation."
 
 #: src/login/org.freedesktop.login1.policy:299
@@ -447,22 +504,22 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:300
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Authentification requise pour mettre le système en hibernation alors que "
 "d'autres utilisateurs sont connectés."
 
 #: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr ""
 "Mettre le système en hibernation alors qu'une application a demandé de "
 "l'empêcher"
 
 #: src/login/org.freedesktop.login1.policy:311
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 "Authentification requise pour mettre le système en hibernation alors qu'une "
 "application a demandé de l'empêcher."
@@ -473,7 +530,7 @@ msgstr "Gérer les sessions actives, les utilisateurs et les postes (seats)"
 
 #: src/login/org.freedesktop.login1.policy:322
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Authentification requise pour gérer les sessions actives, les utilisateurs "
 "et les postes (seats)."
@@ -489,11 +546,20 @@ msgstr ""
 "actives."
 
 #: src/login/org.freedesktop.login1.policy:341
-msgid "Indicate to the firmware to boot to setup interface"
-msgstr ""
-"Indiquer au micrologiciel de démarrer sur l'interface de configuration"
+msgid "Set the reboot \"reason\" in the kernel"
+msgstr "Définir la « raison » du redémarrage dans le noyau"
 
 #: src/login/org.freedesktop.login1.policy:342
+msgid "Authentication is required to set the reboot \"reason\" in the kernel."
+msgstr ""
+"Authentification requise pour définir la « raison » du redémarrage dans le "
+"noyau."
+
+#: src/login/org.freedesktop.login1.policy:352
+msgid "Indicate to the firmware to boot to setup interface"
+msgstr "Indiquer au micrologiciel de démarrer sur l'interface de configuration"
+
+#: src/login/org.freedesktop.login1.policy:353
 msgid ""
 "Authentication is required to indicate to the firmware to boot to setup "
 "interface."
@@ -501,23 +567,23 @@ msgstr ""
 "Authentification requise pour indiquer au micrologiciel de démarrer sur "
 "l'interface de configuration."
 
-#: src/login/org.freedesktop.login1.policy:352
+#: src/login/org.freedesktop.login1.policy:363
 msgid "Indicate to the boot loader to boot to the boot loader menu"
 msgstr "Indiquer au programme d'amorçage d'afficher le menu au démarrage"
 
-#: src/login/org.freedesktop.login1.policy:353
+#: src/login/org.freedesktop.login1.policy:364
 msgid ""
 "Authentication is required to indicate to the boot loader to boot to the "
 "boot loader menu."
 msgstr ""
-"Authentification requise pour indiquer au programme d'amorçage d'afficher "
-"le menu au démarrage."
+"Authentification requise pour indiquer au programme d'amorçage d'afficher le "
+"menu au démarrage."
 
-#: src/login/org.freedesktop.login1.policy:363
+#: src/login/org.freedesktop.login1.policy:374
 msgid "Indicate to the boot loader to boot a specific entry"
 msgstr "Indiquer au programme d'amorçage de démarrer une entrée spécifique"
 
-#: src/login/org.freedesktop.login1.policy:364
+#: src/login/org.freedesktop.login1.policy:375
 msgid ""
 "Authentication is required to indicate to the boot loader to boot into a "
 "specific boot loader entry."
@@ -525,14 +591,22 @@ msgstr ""
 "Authentification requise pour indiquer au programme d'amorçage de démarrer "
 "une entrée spécifique."
 
-#: src/login/org.freedesktop.login1.policy:374
+#: src/login/org.freedesktop.login1.policy:385
 msgid "Set a wall message"
 msgstr "Définir un message wall"
 
-#: src/login/org.freedesktop.login1.policy:375
+#: src/login/org.freedesktop.login1.policy:386
 msgid "Authentication is required to set a wall message"
 msgstr "Authentification requise pour définir un message wall."
 
+#: src/login/org.freedesktop.login1.policy:395
+msgid "Change Session"
+msgstr "Changer de Session"
+
+#: src/login/org.freedesktop.login1.policy:396
+msgid "Authentication is required to change the virtual terminal."
+msgstr "Authentification requise pour changer de terminal virtuel."
+
 #: src/machine/org.freedesktop.machine1.policy:22
 msgid "Log into a local container"
 msgstr "Connexion dans un conteneur local"
@@ -612,6 +686,136 @@ msgstr ""
 "Authentification requise pour gérer les images locales de machines "
 "virtuelles (VM) et de conteneurs."
 
+#: src/network/org.freedesktop.network1.policy:22
+msgid "Set NTP servers"
+msgstr "Définir les serveurs NTP"
+
+#: src/network/org.freedesktop.network1.policy:23
+msgid "Authentication is required to set NTP servers."
+msgstr "Authentification requise pour définir les serveurs NTP."
+
+#: src/network/org.freedesktop.network1.policy:33
+#: src/resolve/org.freedesktop.resolve1.policy:44
+msgid "Set DNS servers"
+msgstr "Définir les serveurs DNS"
+
+#: src/network/org.freedesktop.network1.policy:34
+#: src/resolve/org.freedesktop.resolve1.policy:45
+msgid "Authentication is required to set DNS servers."
+msgstr "Authentification requise pour définir les serveurs DNS."
+
+#: src/network/org.freedesktop.network1.policy:44
+#: src/resolve/org.freedesktop.resolve1.policy:55
+msgid "Set domains"
+msgstr "Définir les domaines"
+
+#: src/network/org.freedesktop.network1.policy:45
+#: src/resolve/org.freedesktop.resolve1.policy:56
+msgid "Authentication is required to set domains."
+msgstr "Authentification requise pour définir les domaines."
+
+#: src/network/org.freedesktop.network1.policy:55
+#: src/resolve/org.freedesktop.resolve1.policy:66
+msgid "Set default route"
+msgstr "Définir la route par défaut"
+
+#: src/network/org.freedesktop.network1.policy:56
+#: src/resolve/org.freedesktop.resolve1.policy:67
+msgid "Authentication is required to set default route."
+msgstr "Authentification requise pour définir la route par défaut."
+
+#: src/network/org.freedesktop.network1.policy:66
+#: src/resolve/org.freedesktop.resolve1.policy:77
+msgid "Enable/disable LLMNR"
+msgstr "Activer/désactiver LLMNR"
+
+#: src/network/org.freedesktop.network1.policy:67
+#: src/resolve/org.freedesktop.resolve1.policy:78
+msgid "Authentication is required to enable or disable LLMNR."
+msgstr "Authentification requise pour activer ou désactiver LLMNR."
+
+#: src/network/org.freedesktop.network1.policy:77
+#: src/resolve/org.freedesktop.resolve1.policy:88
+msgid "Enable/disable multicast DNS"
+msgstr "Activer/désactiver la multidiffusion DNS"
+
+#: src/network/org.freedesktop.network1.policy:78
+#: src/resolve/org.freedesktop.resolve1.policy:89
+msgid "Authentication is required to enable or disable multicast DNS."
+msgstr ""
+"Authentification requise pour activer ou désactiver la multidiffusion DNS."
+
+#: src/network/org.freedesktop.network1.policy:88
+#: src/resolve/org.freedesktop.resolve1.policy:99
+msgid "Enable/disable DNS over TLS"
+msgstr "Activer/désactiver DNS sur TLS"
+
+#: src/network/org.freedesktop.network1.policy:89
+#: src/resolve/org.freedesktop.resolve1.policy:100
+msgid "Authentication is required to enable or disable DNS over TLS."
+msgstr "Authentification requise pour activer ou désactiver DNS sur TLS."
+
+#: src/network/org.freedesktop.network1.policy:99
+#: src/resolve/org.freedesktop.resolve1.policy:110
+msgid "Enable/disable DNSSEC"
+msgstr "Activer/désactiver DNSSEC"
+
+#: src/network/org.freedesktop.network1.policy:100
+#: src/resolve/org.freedesktop.resolve1.policy:111
+msgid "Authentication is required to enable or disable DNSSEC."
+msgstr "Authentification requise pour activer ou désactiver DNSSEC"
+
+#: src/network/org.freedesktop.network1.policy:110
+#: src/resolve/org.freedesktop.resolve1.policy:121
+msgid "Set DNSSEC Negative Trust Anchors"
+msgstr "Définir les Negative Trust Anchors DNSSEC"
+
+#: src/network/org.freedesktop.network1.policy:111
+#: src/resolve/org.freedesktop.resolve1.policy:122
+msgid "Authentication is required to set DNSSEC Negative Trust Anchors."
+msgstr ""
+"Authentification requise pour définir les Negative Trust Anchors DNSSEC."
+
+#: src/network/org.freedesktop.network1.policy:121
+msgid "Revert NTP settings"
+msgstr "Réinitialiser les paramètres NTP"
+
+#: src/network/org.freedesktop.network1.policy:122
+msgid "Authentication is required to reset NTP settings."
+msgstr "Authentification requise pour réinitialiser les paramètres NTP."
+
+#: src/network/org.freedesktop.network1.policy:132
+msgid "Revert DNS settings"
+msgstr "Réinitialiser les paramètres DNS"
+
+#: src/network/org.freedesktop.network1.policy:133
+msgid "Authentication is required to reset DNS settings."
+msgstr "Authentification requise pour réinitialiser les paramètres DNS."
+
+#: src/network/org.freedesktop.network1.policy:143
+msgid "Renew dynamic addresses"
+msgstr "Renouveler les adresses dynamiques"
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to renew dynamic addresses."
+msgstr "Authentification requise pour renouveler les adresses dynamiques."
+
+#: src/network/org.freedesktop.network1.policy:154
+msgid "Reload network settings"
+msgstr "Recharger les paramètres réseau"
+
+#: src/network/org.freedesktop.network1.policy:155
+msgid "Authentication is required to reload network settings."
+msgstr "Authentification requise pour recharger les paramètres réseau."
+
+#: src/network/org.freedesktop.network1.policy:165
+msgid "Reconfigure network interface"
+msgstr "Reconfigurer une interface réseau"
+
+#: src/network/org.freedesktop.network1.policy:166
+msgid "Authentication is required to reconfigure network interface."
+msgstr "Authentification requise pour reconfigurer une interface réseau."
+
 #: src/portable/org.freedesktop.portable1.policy:13
 msgid "Inspect a portable service image"
 msgstr "Inspecter une image de service portable"
@@ -658,6 +862,16 @@ msgstr "Retirer un service DNS-SD"
 msgid "Authentication is required to unregister a DNS-SD service"
 msgstr "Authentification requise pour retirer un service DNS-SD"
 
+#: src/resolve/org.freedesktop.resolve1.policy:132
+msgid "Revert name resolution settings"
+msgstr "Réinitialiser les paramètres de résolution de noms"
+
+#: src/resolve/org.freedesktop.resolve1.policy:133
+msgid "Authentication is required to reset name resolution settings."
+msgstr ""
+"Authentification requise pour réinitialiser les paramètres de résolution de "
+"noms."
+
 #: src/timedate/org.freedesktop.timedate1.policy:22
 msgid "Set system time"
 msgstr "Définir l'heure du système"
@@ -700,23 +914,23 @@ msgstr ""
 "Authentification requise pour activer ou désactiver la synchronisation de "
 "l'heure avec le réseau."
 
-#: src/core/dbus-unit.c:326
+#: src/core/dbus-unit.c:355
 msgid "Authentication is required to start '$(unit)'."
 msgstr "Authentification requise pour démarrer « $(unit) »."
 
-#: src/core/dbus-unit.c:327
+#: src/core/dbus-unit.c:356
 msgid "Authentication is required to stop '$(unit)'."
 msgstr "Authentification requise pour arrêter « $(unit) »."
 
-#: src/core/dbus-unit.c:328
+#: src/core/dbus-unit.c:357
 msgid "Authentication is required to reload '$(unit)'."
 msgstr "Authentification requise pour recharger « $(unit) »."
 
-#: src/core/dbus-unit.c:329 src/core/dbus-unit.c:330
+#: src/core/dbus-unit.c:358 src/core/dbus-unit.c:359
 msgid "Authentication is required to restart '$(unit)'."
 msgstr "Authentification requise pour redémarrer « $(unit) »."
 
-#: src/core/dbus-unit.c:437
+#: src/core/dbus-unit.c:531
 msgid ""
 "Authentication is required to send a UNIX signal to the processes of "
 "'$(unit)'."
@@ -724,16 +938,24 @@ msgstr ""
 "Authentification requise pour envoyer un signal UNIX aux processus de "
 "« $(unit) »."
 
-#: src/core/dbus-unit.c:468
+#: src/core/dbus-unit.c:562
 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
 msgstr ""
 "Authentification requise pour réinitialiser l'état d'« échec » de "
 "« $(unit) »."
 
-#: src/core/dbus-unit.c:501
+#: src/core/dbus-unit.c:595
 msgid "Authentication is required to set properties on '$(unit)'."
 msgstr "Authentification requise pour définir des propriétés de « $(unit) »."
 
+#: src/core/dbus-unit.c:704
+msgid ""
+"Authentication is required to delete files and directories associated with "
+"'$(unit)'."
+msgstr ""
+"Authentification requise pour supprimer les fichiers et les dossiers "
+"associés à « $(unit) »."
+
 #~ msgid "Authentication is required to kill '$(unit)'."
 #~ msgstr "Authentification requise pour tuer « $(unit) »."
 
index 015a65be879aefeed8cce3ee175be27429e73ba1..7796154aa7a495ffa50eed58213a21a25474b138 100644 (file)
--- a/po/gl.po
+++ b/po/gl.po
@@ -280,7 +280,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Permitir conectar anexar a asentos"
 
 #: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr "Requírese autenticación para anexar un dispositivo a un asento."
 
 #: src/login/org.freedesktop.login1.policy:148
@@ -289,7 +289,7 @@ msgstr "Reiniciar os anexos do dispositivo aos asentos"
 
 #: src/login/org.freedesktop.login1.policy:149
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Requírese autenticación para reiniciar como os dispositivos están anexados "
 "aos asentos."
@@ -299,7 +299,7 @@ msgid "Power off the system"
 msgstr "Apagar o sistema"
 
 #: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Requírese autenticación para apagar o sistema."
 
 #: src/login/org.freedesktop.login1.policy:169
@@ -308,20 +308,20 @@ msgstr "Apagar o sistema mentres hai usuarios con unha sesión iniciada"
 
 #: src/login/org.freedesktop.login1.policy:170
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Requírese autenticación para apagar o sistema mentres hai usuarios con unha "
 "sesión iniciada."
 
 #: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "Apagar o sistema cando unha aplicación solicitou a súa inhibición"
 
 #: src/login/org.freedesktop.login1.policy:181
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 "Requírese autenticación para apagar o sistema mentres unha aplicación "
 "solicitou a súa inhibición."
@@ -331,7 +331,7 @@ msgid "Reboot the system"
 msgstr "Reiniciar o sistema"
 
 #: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Requírese autenticación para reiniciar o sistema."
 
 #: src/login/org.freedesktop.login1.policy:202
@@ -340,20 +340,20 @@ msgstr "Reiniciar o sistema mentres outros usuarios teñen unha sesión iniciada
 
 #: src/login/org.freedesktop.login1.policy:203
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "Requírese autenticación para reiniciar o sistema mentres outros usuarios "
 "teñen unha sesión iniciada."
 
 #: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr "Reiniciar o sistema cando unha aplicación solicitou a súa inhibición"
 
 #: src/login/org.freedesktop.login1.policy:214
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 "Requírese autenticación para reiniciar o sistema mentres unha aplicación "
 "solicitou a súa inhibición."
@@ -363,7 +363,7 @@ msgid "Halt the system"
 msgstr "Deter o sistema"
 
 #: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
 msgstr "Requírese autenticación para deter o sistema."
 
 #: src/login/org.freedesktop.login1.policy:235
@@ -372,19 +372,19 @@ msgstr "Deter o sistema mentres outros usuarios teñen unha sesión iniciada"
 
 #: src/login/org.freedesktop.login1.policy:236
 msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt the system while other users are "
 "logged in."
 msgstr ""
 "Requírese autenticación para deter o sistema mentres outros usuarios teñen "
 "unha sesión iniciada."
 
 #: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
 msgstr "Deter o sistema cando unha aplicación solicitou a súa inhibición"
 
 #: src/login/org.freedesktop.login1.policy:247
 msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
 "to inhibit it."
 msgstr ""
 "Requírese autenticación para deter o sistema mentres unha aplicación "
@@ -395,7 +395,7 @@ msgid "Suspend the system"
 msgstr "Suspender o sistema"
 
 #: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Requírese autenticación para suspender o sistema."
 
 #: src/login/org.freedesktop.login1.policy:267
@@ -404,20 +404,20 @@ msgstr "Suspender o sistema mentres outros usuarios teñen unha sesión iniciada
 
 #: src/login/org.freedesktop.login1.policy:268
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Requírese autenticación para suspender o sistema mentres outros usuarios "
 "teñen unha sesión iniciada."
 
 #: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr "Suspender o sistema cando unha aplicación solicitou a súa inhibición"
 
 #: src/login/org.freedesktop.login1.policy:279
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 "Requírese autenticación para suspender o sistema mentres unha aplicación "
 "solicitou a súa inhibición."
@@ -427,7 +427,7 @@ msgid "Hibernate the system"
 msgstr "Hibernar o sistema"
 
 #: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "Requírese autenticación para hibernar o sistema."
 
 #: src/login/org.freedesktop.login1.policy:299
@@ -436,20 +436,20 @@ msgstr "Hibernar o sistema mentres outros usuarios teñen unha sesión iniciada"
 
 #: src/login/org.freedesktop.login1.policy:300
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Requírese autenticación para hibernar o sistema mentres outros usuarios "
 "teñen unha sesión iniciada."
 
 #: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr "Hibernar o sistema cando unha aplicación solicitou a súa inhibición"
 
 #: src/login/org.freedesktop.login1.policy:311
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 "Requírese autenticación para hibernar o sistema mentres unha aplicación "
 "solicitou a súa inhibición."
@@ -460,7 +460,7 @@ msgstr "Xestionar as sesións, usuarios e asentos activos"
 
 #: src/login/org.freedesktop.login1.policy:322
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Requírese autenticación para xestionar as sesións, usuarios e asentos "
 "activos."
index 4c884f18ab32d93c0974cd00d7d922db1b4c8675..d77fee7df939a63c7b14ba1d0c5c1c90416bffc0 100644 (file)
--- a/po/hr.po
+++ b/po/hr.po
@@ -7,50 +7,50 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: systemd master\n"
-"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
-"POT-Creation-Date: 2016-04-27 11:57+0100\n"
-"PO-Revision-Date: 2016-04-27 12:11+0200\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-03-08 12:55+0100\n"
+"PO-Revision-Date: 2020-03-08 12:57+0100\n"
+"Last-Translator: gogo <linux.hr@protonmail.com>\n"
 "Language-Team: \n"
+"Language: hr\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 1.8.7.1\n"
-"Last-Translator: gogo <trebelnik2@gmail.com>com>\n"
+"X-Generator: Poedit 2.0.6\n"
 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-"Language: hr\n"
 
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:1
+#: src/core/org.freedesktop.systemd1.policy.in:22
 msgid "Send passphrase back to system"
 msgstr "Pošalji lozinku natrag u sustav"
 
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:2
+#: src/core/org.freedesktop.systemd1.policy.in:23
 msgid ""
 "Authentication is required to send the entered passphrase back to the system."
 msgstr "Potrebna je ovjera za slanje upisane lozinke natrag u sustav."
 
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:3
+#: src/core/org.freedesktop.systemd1.policy.in:33
 msgid "Manage system services or other units"
 msgstr "Upravljajte uslugama sustava ili drugim jedinicama"
 
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:4
+#: src/core/org.freedesktop.systemd1.policy.in:34
 msgid "Authentication is required to manage system services or other units."
 msgstr "Potrebna je ovjera za upravljanje uslugama sustava ili jedinicama."
 
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:5
+#: src/core/org.freedesktop.systemd1.policy.in:43
 msgid "Manage system service or unit files"
 msgstr "Upravljajte uslugama sustava ili datotekama jedinica"
 
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:6
+#: src/core/org.freedesktop.systemd1.policy.in:44
 msgid "Authentication is required to manage system service or unit files."
 msgstr ""
 "Potrebna je ovjera za upravljanje uslugama sustava ili datotekama jedinica."
 
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:7
+#: src/core/org.freedesktop.systemd1.policy.in:54
 msgid "Set or unset system and service manager environment variables"
 msgstr "Postavite ili uklonite varijable okruženja sustava i usluga"
 
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:8
+#: src/core/org.freedesktop.systemd1.policy.in:55
 msgid ""
 "Authentication is required to set or unset system and service manager "
 "environment variables."
@@ -58,27 +58,78 @@ msgstr ""
 "Potrebna je ovjera za postavljanje ili uklanjanje varijabla okruženja "
 "sustava i usluga."
 
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:9
+#: src/core/org.freedesktop.systemd1.policy.in:64
 msgid "Reload the systemd state"
 msgstr "Ponovno učitaj systemd stanje"
 
-#: ../src/core/org.freedesktop.systemd1.policy.in.in.h:10
+#: src/core/org.freedesktop.systemd1.policy.in:65
 msgid "Authentication is required to reload the systemd state."
-msgstr "Potrebna je ovjera za ponovno učitavanje systemd stanja."
+msgstr "Potrebna je ovjera za ponovno učitavanja systemd stanja."
+
+#: src/home/org.freedesktop.home1.policy:13
+msgid "Create a home area"
+msgstr "Stvori osobni prostor"
+
+#: src/home/org.freedesktop.home1.policy:14
+msgid "Authentication is required to create a user's home area."
+msgstr "Potrebna je ovjera za stvaranje osobnog prostora korisnika."
+
+#: src/home/org.freedesktop.home1.policy:23
+msgid "Remove a home area"
+msgstr "Ukloni osobni prostor"
+
+#: src/home/org.freedesktop.home1.policy:24
+msgid "Authentication is required to remove a user's home area."
+msgstr "Potrebna je ovjera za uklanjanje osobnog prostora korisnika."
+
+#: src/home/org.freedesktop.home1.policy:33
+msgid "Check credentials of a home area"
+msgstr "Provjeri vjerodajnice osobnog prostora"
+
+#: src/home/org.freedesktop.home1.policy:34
+msgid ""
+"Authentication is required to check credentials against a user's home area."
+msgstr ""
+"Potrebna je ovjera za provjeru vjerodajnica osobnog prostora korisnika."
 
-#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
+#: src/home/org.freedesktop.home1.policy:43
+msgid "Update a home area"
+msgstr "Nadopuni osobni prostor"
+
+#: src/home/org.freedesktop.home1.policy:44
+msgid "Authentication is required to update a user's home area."
+msgstr "Potrebna je ovjera za nadopunu osobnog prostora korisnika."
+
+#: src/home/org.freedesktop.home1.policy:53
+msgid "Resize a home area"
+msgstr "Promjeni veličinu osobnog prostora korisnika"
+
+#: src/home/org.freedesktop.home1.policy:54
+msgid "Authentication is required to resize a user's home area."
+msgstr "Potrebna je ovjera za promjenu veličine osobnog prostora korisnika."
+
+#: src/home/org.freedesktop.home1.policy:63
+msgid "Change password of a home area"
+msgstr "Promijeni lozinku osobnog prostora"
+
+#: src/home/org.freedesktop.home1.policy:64
+msgid ""
+"Authentication is required to change the password of a user's home area."
+msgstr "Potrebna je ovjera za promjenu lozinke osobnog prostora korisnika."
+
+#: src/hostname/org.freedesktop.hostname1.policy:20
 msgid "Set host name"
 msgstr "Postavi naziv računala"
 
-#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2
+#: src/hostname/org.freedesktop.hostname1.policy:21
 msgid "Authentication is required to set the local host name."
 msgstr "Potrebna je ovjera za postavljanje naziva lokalnog računala."
 
-#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3
+#: src/hostname/org.freedesktop.hostname1.policy:30
 msgid "Set static host name"
 msgstr "Postavi nepromjenjivi naziv račumala"
 
-#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4
+#: 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."
@@ -86,416 +137,507 @@ msgstr ""
 "Potrebna je ovjera za postavljenje nepromjenjivog naziva lokalnog računala, "
 "kao i prijatnog naziva računala."
 
-#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:5
+#: src/hostname/org.freedesktop.hostname1.policy:41
 msgid "Set machine information"
 msgstr "Postavi informacije računala"
 
-#: ../src/hostname/org.freedesktop.hostname1.policy.in.h:6
+#: src/hostname/org.freedesktop.hostname1.policy:42
 msgid "Authentication is required to set local machine information."
 msgstr "Potrebna je ovjera za postavljanje informacije lokalnog računala."
 
-#: ../src/import/org.freedesktop.import1.policy.in.h:1
+#: src/hostname/org.freedesktop.hostname1.policy:51
+msgid "Get product UUID"
+msgstr "Prikaži UUID proizvoda"
+
+#: src/hostname/org.freedesktop.hostname1.policy:52
+msgid "Authentication is required to get product UUID."
+msgstr "Potrebna je ovjera za prikaz UUID-a proizvoda."
+
+#: src/import/org.freedesktop.import1.policy:22
 msgid "Import a VM or container image"
 msgstr "Uvezi VM ili spremnik slike"
 
-#: ../src/import/org.freedesktop.import1.policy.in.h:2
+#: src/import/org.freedesktop.import1.policy:23
 msgid "Authentication is required to import a VM or container image"
 msgstr "Potrebna je ovjera za uvoz WM ili spremnika slike"
 
-#: ../src/import/org.freedesktop.import1.policy.in.h:3
+#: src/import/org.freedesktop.import1.policy:32
 msgid "Export a VM or container image"
 msgstr "Izvezi VM ili spremnik slike"
 
-#: ../src/import/org.freedesktop.import1.policy.in.h:4
+#: src/import/org.freedesktop.import1.policy:33
 msgid "Authentication is required to export a VM or container image"
 msgstr "Potrebna je ovjera za izvoz WM ili spremnika slike"
 
-#: ../src/import/org.freedesktop.import1.policy.in.h:5
+#: src/import/org.freedesktop.import1.policy:42
 msgid "Download a VM or container image"
 msgstr "Preuzmi VM ili spremnik slike"
 
-#: ../src/import/org.freedesktop.import1.policy.in.h:6
+#: src/import/org.freedesktop.import1.policy:43
 msgid "Authentication is required to download a VM or container image"
 msgstr "Potrebna je ovjera za preuzimanje VM ili spremnika slike."
 
-#: ../src/locale/org.freedesktop.locale1.policy.in.h:1
+#: src/locale/org.freedesktop.locale1.policy:22
 msgid "Set system locale"
 msgstr "Postavi sustav lokalizacije"
 
-#: ../src/locale/org.freedesktop.locale1.policy.in.h:2
+#: src/locale/org.freedesktop.locale1.policy:23
 msgid "Authentication is required to set the system locale."
 msgstr "Potrebna je ovjera za postavljanje sustava lokalizacije."
 
-#: ../src/locale/org.freedesktop.locale1.policy.in.h:3
+#: src/locale/org.freedesktop.locale1.policy:33
 msgid "Set system keyboard settings"
 msgstr "Postavi postavke tipkovnice sustava"
 
-#: ../src/locale/org.freedesktop.locale1.policy.in.h:4
+#: src/locale/org.freedesktop.locale1.policy:34
 msgid "Authentication is required to set the system keyboard settings."
 msgstr "Potrebna je ovjera za postavljanje postavki tipkovnice sustava."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:1
+#: src/login/org.freedesktop.login1.policy:22
 msgid "Allow applications to inhibit system shutdown"
-msgstr "Dopusti aplikacijama zaustavljanje isključivanja sustava"
+msgstr "Dopusti aplikacijama spriječavanje isključivanja sustava"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:2
+#: src/login/org.freedesktop.login1.policy:23
 msgid ""
 "Authentication is required for an application to inhibit system shutdown."
 msgstr ""
-"Potrebna je ovjera za dopuštanje aplikacijama zaustavljanje isključivanja "
+"Potrebna je ovjera za dopuštanje aplikacijama u spriječavanju isključivanja "
 "sustava."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:3
+#: src/login/org.freedesktop.login1.policy:33
 msgid "Allow applications to delay system shutdown"
 msgstr "Dopusti aplikacijama odgodu isključivanja sustava"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:4
+#: src/login/org.freedesktop.login1.policy:34
 msgid "Authentication is required for an application to delay system shutdown."
 msgstr ""
-"Potrebna je ovjera za dopuštanje aplikacijama odgode isključivanja sustava."
+"Potrebna je ovjera za dopuštanje aplikacijama u odgodi isključivanja sustava."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:5
+#: src/login/org.freedesktop.login1.policy:44
 msgid "Allow applications to inhibit system sleep"
-msgstr "Dopusti aplikacijama zaustavljanje spavanja sustava"
+msgstr "Dopusti aplikacijama spriječavanje spavanja sustava"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:6
+#: src/login/org.freedesktop.login1.policy:45
 msgid "Authentication is required for an application to inhibit system sleep."
 msgstr ""
-"Potrebna je ovjera za dopuštanje aplikacijama zaustavljanja spavanja sustava."
+"Potrebna je ovjera za dopuštanje aplikacijama u spriječavanju spavanja "
+"sustava."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:7
+#: src/login/org.freedesktop.login1.policy:55
 msgid "Allow applications to delay system sleep"
 msgstr "Dopusti aplikacijama odgodu spavanja sustava"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:8
+#: src/login/org.freedesktop.login1.policy:56
 msgid "Authentication is required for an application to delay system sleep."
 msgstr "Potrebna je ovjera za odgodu spavanja sustava."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:9
+#: src/login/org.freedesktop.login1.policy:65
 msgid "Allow applications to inhibit automatic system suspend"
-msgstr "Dopusti aplikacijama zaustavljanje automatskog suspendiranja sustava"
+msgstr "Dopusti aplikacijama spriječavanje automatskog suspendiranja sustava"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:10
+#: src/login/org.freedesktop.login1.policy:66
 msgid ""
 "Authentication is required for an application to inhibit automatic system "
 "suspend."
 msgstr ""
-"Potrebna je ovjera za dopuštanje aplikacijama zaustavljanje automatskog "
+"Potrebna je ovjera za dopuštanje aplikacijama u spriječavanju automatskog "
 "suspendiranja sustava."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:11
+#: src/login/org.freedesktop.login1.policy:75
 msgid "Allow applications to inhibit system handling of the power key"
 msgstr ""
-"Dopusti aplikacijama sprječavanje rukovanja sustava tipkom isključivanja"
+"Dopusti aplikacijama spriječavanje rukovanja sustava tipkom isključivanja"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:12
+#: src/login/org.freedesktop.login1.policy:76
 msgid ""
 "Authentication is required for an application to inhibit system handling of "
 "the power key."
 msgstr ""
-"Potrebna je ovjera za dopuštanje aplikacijama sprječavanje rukovanja sustava "
-"tipkom isključivanja."
+"Potrebna je ovjera za dopuštanje aplikacijama u spriječavanju rukovanja "
+"sustava tipkom isključivanja."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:13
+#: src/login/org.freedesktop.login1.policy:86
 msgid "Allow applications to inhibit system handling of the suspend key"
-msgstr "Dopusti aplikacijama sprječavanje rukovanja sustava tipkom suspenzije"
+msgstr "Dopusti aplikacijama spriječavanje rukovanja sustava tipkom suspenzije"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:14
+#: src/login/org.freedesktop.login1.policy:87
 msgid ""
 "Authentication is required for an application to inhibit system handling of "
 "the suspend key."
 msgstr ""
-"Potrebna je ovjera za dopuštanje aplikacijama sprječavanje rukovanja sustava "
-"tipkom suspenzije."
+"Potrebna je ovjera za dopuštanje aplikacijama u spriječavanju rukovanja "
+"sustava tipkom suspenzije."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:15
+#: src/login/org.freedesktop.login1.policy:97
 msgid "Allow applications to inhibit system handling of the hibernate key"
-msgstr "Dopusti aplikacijama sprječavanje rukovanja sustava tipkom hibernacije"
+msgstr ""
+"Dopusti aplikacijama spriječavanje rukovanja sustava tipkom hibernacije"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:16
+#: src/login/org.freedesktop.login1.policy:98
 msgid ""
 "Authentication is required for an application to inhibit system handling of "
 "the hibernate key."
 msgstr ""
-"Potrebna je ovjera za dopuštanje aplikacijama sprječavanje rukovanja sustava "
-"tipkom hibernacije."
+"Potrebna je ovjera za dopuštanje aplikacijama u spriječavanju rukovanja "
+"sustava tipkom hibernacije."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:17
+#: src/login/org.freedesktop.login1.policy:107
 msgid "Allow applications to inhibit system handling of the lid switch"
-msgstr "Dopusti aplikacijama sprječavanje rukovanja sustava preklopnicama"
+msgstr "Dopusti aplikacijama spriječavanje rukovanja sustava preklopnicama"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:18
+#: src/login/org.freedesktop.login1.policy:108
 msgid ""
 "Authentication is required for an application to inhibit system handling of "
 "the lid switch."
 msgstr ""
-"Potrebna je ovjera za dopuštenje sprječavanja rukovanja sustava "
+"Potrebna je ovjera za dopuštanje spriječavanja rukovanja sustava "
 "preklopnicama."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:19
+#: src/login/org.freedesktop.login1.policy:117
+msgid "Allow non-logged-in user to run programs"
+msgstr "Dopusti neprijavljenim korisnicima pokretanje programa"
+
+#: src/login/org.freedesktop.login1.policy:118
+msgid "Explicit request is required to run programs as a non-logged-in user."
+msgstr ""
+"Potrebna je ovjera za dopuštanje neprijavljenim korisnicima pokretanje "
+"programa."
+
+#: src/login/org.freedesktop.login1.policy:127
 msgid "Allow non-logged-in users to run programs"
 msgstr "Dopusti neprijavljenim korisnicima pokretanje programa"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:20
+#: src/login/org.freedesktop.login1.policy:128
 msgid "Authentication is required to run programs as a non-logged-in user."
 msgstr ""
-"Potrebna je ovjera za dopuštenje neprijavljenim korisnicima pokretanje "
+"Potrebna je ovjera za dopuštanje neprijavljenim korisnicima pokretanje "
 "programa."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:21
+#: src/login/org.freedesktop.login1.policy:137
 msgid "Allow attaching devices to seats"
 msgstr "Dopusti povezivanje uređaja skupu sesija i hardvera"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:22
-msgid "Authentication is required for attaching a device to a seat."
+#: src/login/org.freedesktop.login1.policy:138
+msgid "Authentication is required to attach a device to a seat."
 msgstr "Potrebna je ovjera za povezivanje uređaja sa skupom sesija i hardvera."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:23
+#: src/login/org.freedesktop.login1.policy:148
 msgid "Flush device to seat attachments"
 msgstr "Ukloni povezani uređaj sa skupa sesija i hardvera"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:24
-msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+#: src/login/org.freedesktop.login1.policy:149
+msgid "Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Potrebna je ovjera za obnovu povezivanja uređaja sa skupom sesija i hardvera."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:25
+#: src/login/org.freedesktop.login1.policy:158
 msgid "Power off the system"
 msgstr "Isključi sustav"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:26
-msgid "Authentication is required for powering off the system."
+#: src/login/org.freedesktop.login1.policy:159
+msgid "Authentication is required to power off the system."
 msgstr "Potrebna je ovjera za isključivanje sustava."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:27
+#: src/login/org.freedesktop.login1.policy:169
 msgid "Power off the system while other users are logged in"
 msgstr "Isključi sustav kada su ostali korisnici prijavljeni"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:28
+#: src/login/org.freedesktop.login1.policy:170
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Potrebna je ovjera za isključivanje sustava kada su ostali korisnici "
 "prijavljeni."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:29
-msgid "Power off the system while an application asked to inhibit it"
+#: src/login/org.freedesktop.login1.policy:180
+msgid "Power off the system while an application is inhibiting this"
 msgstr ""
-"Isključi sustav kada je aplikacija zatražila zaustavljanje isključivanja"
+"Isključi sustav kada je aplikacija zatražila spriječavanje isključivanja"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:30
+#: src/login/org.freedesktop.login1.policy:181
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application is "
+"inhibiting this."
 msgstr ""
 "Potrebna je ovjera za isključivanje sustava kada je aplikacija zatražila "
-"zaustavljanje isključivanja."
+"spriječavanje isključivanja."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:31
+#: src/login/org.freedesktop.login1.policy:191
 msgid "Reboot the system"
 msgstr "Ponovno pokreni sustav"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:32
-msgid "Authentication is required for rebooting the system."
+#: src/login/org.freedesktop.login1.policy:192
+msgid "Authentication is required to reboot the system."
 msgstr "Potrebna je ovjera za ponovno pokretanje sustava."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:33
+#: src/login/org.freedesktop.login1.policy:202
 msgid "Reboot the system while other users are logged in"
 msgstr "Ponovno pokreni sustav kada su ostali korisnici prijavljeni"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:34
+#: src/login/org.freedesktop.login1.policy:203
 msgid ""
-"Authentication is required for rebooting the system while other users are "
-"logged in."
+"Authentication is required to reboot the system while other users are logged "
+"in."
 msgstr ""
 "Potrebna je ovjera za ponovno pokretanje sustava kada su ostali korisnici "
 "prijavljeni."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:35
-msgid "Reboot the system while an application asked to inhibit it"
+#: src/login/org.freedesktop.login1.policy:213
+msgid "Reboot the system while an application is inhibiting this"
 msgstr ""
-"Ponovno pokreni sustav kada je aplikacija zatražila zaustavljanje ponovnog "
+"Ponovno pokreni sustav kada je aplikacija zatražila spriječavanje ponovnog "
 "pokretanja"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:36
+#: src/login/org.freedesktop.login1.policy:214
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application is "
+"inhibiting this."
 msgstr ""
 "Potrebna je ovjera za ponovno pokretanje sustava kada je aplikacija "
-"zatražila zaustavljanje ponovnog pokretanja."
+"zatražila spriječavanje ponovnog pokretanja."
+
+#: src/login/org.freedesktop.login1.policy:224
+msgid "Halt the system"
+msgstr "Zaustavi sustav"
+
+#: src/login/org.freedesktop.login1.policy:225
+msgid "Authentication is required to halt the system."
+msgstr "Potrebna je ovjera za zaustavljanje sustava."
+
+#: src/login/org.freedesktop.login1.policy:235
+msgid "Halt the system while other users are logged in"
+msgstr "Zaustavi sustav kada su ostali korisnici prijavljeni"
+
+#: src/login/org.freedesktop.login1.policy:236
+msgid ""
+"Authentication is required to halt the system while other users are logged "
+"in."
+msgstr ""
+"Potrebna je ovjera za zaustavljanje sustava kada su drugi korisnici "
+"prijavljeni."
+
+#: src/login/org.freedesktop.login1.policy:246
+msgid "Halt the system while an application is inhibiting this"
+msgstr ""
+"Zaustavi sustav kada je aplikacija zatražila spriječavanje zaustavljanja"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:37
+#: src/login/org.freedesktop.login1.policy:247
+msgid ""
+"Authentication is required to halt the system while an application is "
+"inhibiting this."
+msgstr ""
+"Potrebna je ovjera za zaustavljanje sustava kada je aplikacija zatražila "
+"spriječavanje hibernacije."
+
+#: src/login/org.freedesktop.login1.policy:257
 msgid "Suspend the system"
 msgstr "Suspendiraj sustav"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:38
-msgid "Authentication is required for suspending the system."
+#: src/login/org.freedesktop.login1.policy:258
+msgid "Authentication is required to suspend the system."
 msgstr "Potrebna je ovjera za suspendiranje sustava."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:39
+#: src/login/org.freedesktop.login1.policy:267
 msgid "Suspend the system while other users are logged in"
 msgstr "Suspendiraj sustav kada su drugi korisnici prijavljeni"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:40
+#: src/login/org.freedesktop.login1.policy:268
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Potrebna je ovjera za suspendiranje sustava kada su drugi korisnici "
 "prijavljeni."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:41
-msgid "Suspend the system while an application asked to inhibit it"
+#: src/login/org.freedesktop.login1.policy:278
+msgid "Suspend the system while an application is inhibiting this"
 msgstr ""
-"Suspendiraj sustav kada je aplikacija zatražila zaustavljanje suspendiranja"
+"Suspendiraj sustav kada je aplikacija zatražila spriječavanje suspendiranja"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:42
+#: src/login/org.freedesktop.login1.policy:279
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application is "
+"inhibiting this."
 msgstr ""
 "Potrebna je ovjera za suspendiranje sustava kada je aplikacija zatražila "
-"zaustavljanje suspendiranja."
+"spriječavanje suspendiranja."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:43
+#: src/login/org.freedesktop.login1.policy:289
 msgid "Hibernate the system"
 msgstr "Hiberniraj sustav"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:44
-msgid "Authentication is required for hibernating the system."
+#: src/login/org.freedesktop.login1.policy:290
+msgid "Authentication is required to hibernate the system."
 msgstr "Potrebna je ovjera za hibernaciju sustava."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:45
+#: src/login/org.freedesktop.login1.policy:299
 msgid "Hibernate the system while other users are logged in"
-msgstr "Hiberniraj sustav kada su ostali korisnici prijavljeni."
+msgstr "Hiberniraj sustav kada su ostali korisnici prijavljeni"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:46
+#: src/login/org.freedesktop.login1.policy:300
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Potrebna je ovjera za hibernaciju sustava kada su drugi korisnici "
 "prijavljeni."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:47
-msgid "Hibernate the system while an application asked to inhibit it"
+#: src/login/org.freedesktop.login1.policy:310
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr ""
-"Hiberniraj sustav kada je aplikacija zatražila zaustavljanje hibernacije"
+"Hiberniraj sustav kada je aplikacija zatražila spriječavanje hibernacije"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:48
+#: src/login/org.freedesktop.login1.policy:311
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application is "
+"inhibiting this."
 msgstr ""
 "Potrebna je ovjera za hibernaciju sustava kada je aplikacija zatražila "
-"zaustavljanje hibernacije."
+"spriječavanje hibernacije."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:49
+#: src/login/org.freedesktop.login1.policy:321
 msgid "Manage active sessions, users and seats"
 msgstr ""
 "Upravljanje aktivnim sesijama, korisnicima i skupovima sesija i hardvera"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:50
-msgid ""
-"Authentication is required for managing active sessions, users and seats."
+#: src/login/org.freedesktop.login1.policy:322
+msgid "Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Potrebna je ovjera za upravljanje aktivnim sesijama, korisnicima i skupovima "
 "sesija i hardvera."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:51
+#: src/login/org.freedesktop.login1.policy:331
 msgid "Lock or unlock active sessions"
 msgstr "Zaključavanje ili otključavanje aktivne sesije"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:52
+#: src/login/org.freedesktop.login1.policy:332
 msgid "Authentication is required to lock or unlock active sessions."
 msgstr "Potrebna je ovjera za zaključavanje ili otključavanje aktivne sesije."
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:53
-msgid "Allow indication to the firmware to boot to setup interface"
-msgstr "Dopusti najavu frimveru za pokretanje sučelja postavljanja"
+#: src/login/org.freedesktop.login1.policy:341
+msgid "Set the reboot \"reason\" in the kernel"
+msgstr "Postavi \"reason\" ponovnog pokretanja u kernel"
+
+#: src/login/org.freedesktop.login1.policy:342
+msgid "Authentication is required to set the reboot \"reason\" in the kernel."
+msgstr ""
+"Potrebna je ovjera za postaviti \"reason\" ponovnog pokretanja u kernelu."
+
+#: src/login/org.freedesktop.login1.policy:352
+msgid "Indicate to the firmware to boot to setup interface"
+msgstr "Najavi firmveru da pokrene sučelje postavljanja"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:54
+#: src/login/org.freedesktop.login1.policy:353
 msgid ""
 "Authentication is required to indicate to the firmware to boot to setup "
 "interface."
-msgstr "Potrebna je ovjera najave frimvera za pokretanje sučelja postavljanja."
+msgstr "Potrebna je ovjera za najavu firmveru da pokrene sučelje postavljanja."
+
+#: src/login/org.freedesktop.login1.policy:363
+msgid "Indicate to the boot loader to boot to the boot loader menu"
+msgstr "Najavi učitaču pokretanja da pokrene izbornik učitača pokretanja"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:55
+#: src/login/org.freedesktop.login1.policy:364
+msgid ""
+"Authentication is required to indicate to the boot loader to boot to the "
+"boot loader menu."
+msgstr ""
+"Potrebna je ovjera za najavu učitaču pokretanja da pokrene izbornik učitača "
+"pokretanja."
+
+#: src/login/org.freedesktop.login1.policy:374
+msgid "Indicate to the boot loader to boot a specific entry"
+msgstr "Najavi učitaču pokretanja da pokrene određeni unos"
+
+#: src/login/org.freedesktop.login1.policy:375
+msgid ""
+"Authentication is required to indicate to the boot loader to boot into a "
+"specific boot loader entry."
+msgstr ""
+"Potrebna je ovjera za najavu učitaču pokretanja da pokrene određeni unos."
+
+#: src/login/org.freedesktop.login1.policy:385
 msgid "Set a wall message"
 msgstr "Postavljanje zaslonske pruke"
 
-#: ../src/login/org.freedesktop.login1.policy.in.h:56
+#: src/login/org.freedesktop.login1.policy:386
 msgid "Authentication is required to set a wall message"
 msgstr "Potrebna je ovjera za postavljanje zaslonske pruke."
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:1
+#: src/login/org.freedesktop.login1.policy:395
+msgid "Change Session"
+msgstr "Promijeni sesiju"
+
+#: src/login/org.freedesktop.login1.policy:396
+msgid "Authentication is required to change the virtual terminal."
+msgstr "Potrebna je ovjera za promjenu virtualnog terminala."
+
+#: src/machine/org.freedesktop.machine1.policy:22
 msgid "Log into a local container"
 msgstr "Prijavi se u lokalni spremnik"
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:2
+#: src/machine/org.freedesktop.machine1.policy:23
 msgid "Authentication is required to log into a local container."
 msgstr "Potrebna je ovjera za prijavu u lokalni spremnik."
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:3
+#: src/machine/org.freedesktop.machine1.policy:32
 msgid "Log into the local host"
 msgstr "Prijava na lokalno računalo"
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:4
+#: src/machine/org.freedesktop.machine1.policy:33
 msgid "Authentication is required to log into the local host."
 msgstr "Potrebna je ovjera za prijavu na lokalno račuanlo."
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:5
+#: src/machine/org.freedesktop.machine1.policy:42
 msgid "Acquire a shell in a local container"
 msgstr "Pokretanje ljuske u lokalnom spremniku"
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:6
+#: src/machine/org.freedesktop.machine1.policy:43
 msgid "Authentication is required to acquire a shell in a local container."
 msgstr "Potrebna je ovjera za pokretanje ljuske u lokalnom spremniku."
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:7
+#: src/machine/org.freedesktop.machine1.policy:53
 msgid "Acquire a shell on the local host"
 msgstr "Pokretanje ljuske na lokalnom računalu"
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:8
+#: src/machine/org.freedesktop.machine1.policy:54
 msgid "Authentication is required to acquire a shell on the local host."
 msgstr "Potrebna je ovjera za pokretanje ljuske na lokalnom računalu."
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:9
+#: src/machine/org.freedesktop.machine1.policy:64
 msgid "Acquire a pseudo TTY in a local container"
 msgstr "Pokretanje pseudo TTY na lokalnom spremniku"
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:10
+#: src/machine/org.freedesktop.machine1.policy:65
 msgid ""
 "Authentication is required to acquire a pseudo TTY in a local container."
 msgstr "Potrebna je ovjera za pokretanje pseudo TTY na lokalnom spremniku."
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:11
+#: src/machine/org.freedesktop.machine1.policy:74
 msgid "Acquire a pseudo TTY on the local host"
 msgstr "Pokretanje pseudo TTY na lokalnom računalu"
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:12
+#: src/machine/org.freedesktop.machine1.policy:75
 msgid "Authentication is required to acquire a pseudo TTY on the local host."
 msgstr "Potrebna je ovjera za pokretanje pseudo TTY na lokalnom računalu."
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:13
+#: src/machine/org.freedesktop.machine1.policy:84
 msgid "Manage local virtual machines and containers"
 msgstr "Upravljanje lokalnim vurtualnim strojevima i spremnicima"
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:14
+#: src/machine/org.freedesktop.machine1.policy:85
 msgid ""
 "Authentication is required to manage local virtual machines and containers."
 msgstr ""
 "Potrebna je ovjera za upravljanje lokalnim vurtualnim strojevima i "
 "spremnicima."
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:15
+#: src/machine/org.freedesktop.machine1.policy:95
 msgid "Manage local virtual machine and container images"
 msgstr "Upravljanje lokalnim vurtualnim strojevima i spremnicima slika"
 
-#: ../src/machine/org.freedesktop.machine1.policy.in.h:16
+#: src/machine/org.freedesktop.machine1.policy:96
 msgid ""
 "Authentication is required to manage local virtual machine and container "
 "images."
@@ -503,38 +645,227 @@ msgstr ""
 "Potrebna je ovjera za upravljanje lokalnim vurtualnim strojevima i "
 "spremnicima slika."
 
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:1
+#: src/network/org.freedesktop.network1.policy:22
+msgid "Set NTP servers"
+msgstr "Postavi NTP poslužitelje"
+
+#: src/network/org.freedesktop.network1.policy:23
+msgid "Authentication is required to set NTP servers."
+msgstr "Potrebna je ovjera za postavljanje NTP poslužitelja."
+
+#: src/network/org.freedesktop.network1.policy:33
+#: src/resolve/org.freedesktop.resolve1.policy:44
+msgid "Set DNS servers"
+msgstr "Postavi DNS poslužitelje"
+
+#: src/network/org.freedesktop.network1.policy:34
+#: src/resolve/org.freedesktop.resolve1.policy:45
+msgid "Authentication is required to set DNS servers."
+msgstr "Potrebna je ovjera za postavljanje DNS poslužitelja."
+
+#: src/network/org.freedesktop.network1.policy:44
+#: src/resolve/org.freedesktop.resolve1.policy:55
+msgid "Set domains"
+msgstr "Postavi domene"
+
+#: src/network/org.freedesktop.network1.policy:45
+#: src/resolve/org.freedesktop.resolve1.policy:56
+msgid "Authentication is required to set domains."
+msgstr "Potrebna je ovjera za postavljanje domena."
+
+#: src/network/org.freedesktop.network1.policy:55
+#: src/resolve/org.freedesktop.resolve1.policy:66
+msgid "Set default route"
+msgstr "Postavi uobičajenu rutu"
+
+#: src/network/org.freedesktop.network1.policy:56
+#: src/resolve/org.freedesktop.resolve1.policy:67
+msgid "Authentication is required to set default route."
+msgstr "Potrebna je ovjera za postavljanje uobičajene rute."
+
+#: src/network/org.freedesktop.network1.policy:66
+#: src/resolve/org.freedesktop.resolve1.policy:77
+msgid "Enable/disable LLMNR"
+msgstr "Omogući/Onemogući LLMNR"
+
+#: src/network/org.freedesktop.network1.policy:67
+#: src/resolve/org.freedesktop.resolve1.policy:78
+msgid "Authentication is required to enable or disable LLMNR."
+msgstr "Potrebna je ovjera za LLMNR omogućavanje ili onemogućavanje."
+
+#: src/network/org.freedesktop.network1.policy:77
+#: src/resolve/org.freedesktop.resolve1.policy:88
+msgid "Enable/disable multicast DNS"
+msgstr "Omogući/Onemogući glatko osvježavanje"
+
+#: src/network/org.freedesktop.network1.policy:78
+#: src/resolve/org.freedesktop.resolve1.policy:89
+msgid "Authentication is required to enable or disable multicast DNS."
+msgstr ""
+"Potrebna je ovjera za omogućavanje ili onemogućavanje DNS grupnog emitiranja."
+
+#: src/network/org.freedesktop.network1.policy:88
+#: src/resolve/org.freedesktop.resolve1.policy:99
+msgid "Enable/disable DNS over TLS"
+msgstr "Omogući/Onemogući DNS putem TLS-a"
+
+#: src/network/org.freedesktop.network1.policy:89
+#: src/resolve/org.freedesktop.resolve1.policy:100
+msgid "Authentication is required to enable or disable DNS over TLS."
+msgstr "Potrebna je ovjera za omogućavanje/onemogućavanje DNS-a putem TLS-a."
+
+#: src/network/org.freedesktop.network1.policy:99
+#: src/resolve/org.freedesktop.resolve1.policy:110
+msgid "Enable/disable DNSSEC"
+msgstr "Omogući/Onemogući DNSSEC"
+
+#: src/network/org.freedesktop.network1.policy:100
+#: src/resolve/org.freedesktop.resolve1.policy:111
+msgid "Authentication is required to enable or disable DNSSEC."
+msgstr "Potrebna je ovjera za DNSSEC omogućavanje ili onemogućavanje."
+
+#: src/network/org.freedesktop.network1.policy:110
+#: src/resolve/org.freedesktop.resolve1.policy:121
+msgid "Set DNSSEC Negative Trust Anchors"
+msgstr "Postavi DNSSEC negativna pouzdana sidrišta"
+
+#: src/network/org.freedesktop.network1.policy:111
+#: src/resolve/org.freedesktop.resolve1.policy:122
+msgid "Authentication is required to set DNSSEC Negative Trust Anchors."
+msgstr ""
+"Potrebna je ovjera za postavljanje DNSSEC negativnih pouzdanih sidrišta."
+
+#: src/network/org.freedesktop.network1.policy:121
+msgid "Revert NTP settings"
+msgstr "Vrati izvorne NTP postavke"
+
+#: src/network/org.freedesktop.network1.policy:122
+msgid "Authentication is required to reset NTP settings."
+msgstr "Potrebna je ovjera za vraćanje izvornih NTP postavki."
+
+#: src/network/org.freedesktop.network1.policy:132
+msgid "Revert DNS settings"
+msgstr "Vrati izvorne DNS postavke"
+
+#: src/network/org.freedesktop.network1.policy:133
+msgid "Authentication is required to reset DNS settings."
+msgstr "Potrebna je ovjera za vraćanje izvornih DNS postavki."
+
+#: src/network/org.freedesktop.network1.policy:143
+msgid "DHCP server sends force renew message"
+msgstr "DHCP poslužitelj šalje poruku prisilne ponovne uspostave"
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to send force renew message."
+msgstr "Potrebna je ovjera za slanje poruke prisilne ponovne uspostave."
+
+#: src/network/org.freedesktop.network1.policy:154
+msgid "Renew dynamic addresses"
+msgstr "Ponovno uspostavi promjenjive adrese"
+
+#: src/network/org.freedesktop.network1.policy:155
+msgid "Authentication is required to renew dynamic addresses."
+msgstr "Potrebna je ovjera za ponovno uspostavljanje promjenjivih adresa."
+
+#: src/network/org.freedesktop.network1.policy:165
+msgid "Reload network settings"
+msgstr "Ponovno učitaj postavke mreže"
+
+#: src/network/org.freedesktop.network1.policy:166
+msgid "Authentication is required to reload network settings."
+msgstr "Potrebna je ovjera za ponovno učitavanje postavka mreže."
+
+#: src/network/org.freedesktop.network1.policy:176
+msgid "Reconfigure network interface"
+msgstr "Ponovno podesi mrežno sučelje"
+
+#: src/network/org.freedesktop.network1.policy:177
+msgid "Authentication is required to reconfigure network interface."
+msgstr "Potrebna je ovjera za ponovno podešavanje mrežnog sučelja."
+
+#: src/portable/org.freedesktop.portable1.policy:13
+msgid "Inspect a portable service image"
+msgstr "Provjeri prijenosnu sliku usluge"
+
+#: src/portable/org.freedesktop.portable1.policy:14
+msgid "Authentication is required to inspect a portable service image."
+msgstr "Potrebna je ovjera za provjeru prijenosne slike usluge."
+
+#: src/portable/org.freedesktop.portable1.policy:23
+msgid "Attach or detach a portable service image"
+msgstr "Dodaj ili ukloni prijenosnu sliku usluge"
+
+#: src/portable/org.freedesktop.portable1.policy:24
+msgid ""
+"Authentication is required to attach or detach a portable service image."
+msgstr ""
+"Potrebna je ovjera za dodavanje ili uklanjanje prijenosne slike usluge."
+
+#: src/portable/org.freedesktop.portable1.policy:34
+msgid "Delete or modify portable service image"
+msgstr "Obriši ili promijeni prijenosnu sliku usluge"
+
+#: src/portable/org.freedesktop.portable1.policy:35
+msgid ""
+"Authentication is required to delete or modify a portable service image."
+msgstr "Potrebna je ovjera za brisnje ili promijenu prijenosne slike usluge."
+
+#: src/resolve/org.freedesktop.resolve1.policy:22
+msgid "Register a DNS-SD service"
+msgstr "Registriraj DNS-SD uslugu"
+
+#: src/resolve/org.freedesktop.resolve1.policy:23
+msgid "Authentication is required to register a DNS-SD service"
+msgstr "Potrebna je ovjera za registriranje DNS-SD usluge."
+
+#: src/resolve/org.freedesktop.resolve1.policy:33
+msgid "Unregister a DNS-SD service"
+msgstr "Ukloni registriraciju DNS-SD usluge"
+
+#: src/resolve/org.freedesktop.resolve1.policy:34
+msgid "Authentication is required to unregister a DNS-SD service"
+msgstr "Potrebna je ovjera za uklanjanje registracije DNS-SD usluge."
+
+#: src/resolve/org.freedesktop.resolve1.policy:132
+msgid "Revert name resolution settings"
+msgstr "Vrati izvorni naziv postavki razlučivosti"
+
+#: src/resolve/org.freedesktop.resolve1.policy:133
+msgid "Authentication is required to reset name resolution settings."
+msgstr "Potrebna je ovjera za vraćanje izvornog naziva postavki razlučivosti."
+
+#: src/timedate/org.freedesktop.timedate1.policy:22
 msgid "Set system time"
 msgstr "Postavi vrijeme sustava"
 
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:2
+#: src/timedate/org.freedesktop.timedate1.policy:23
 msgid "Authentication is required to set the system time."
 msgstr "Potrebna je ovjera za postavljanje vremena sustava."
 
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:3
+#: src/timedate/org.freedesktop.timedate1.policy:33
 msgid "Set system timezone"
 msgstr "Postavi vremensku zonu sustava"
 
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:4
+#: src/timedate/org.freedesktop.timedate1.policy:34
 msgid "Authentication is required to set the system timezone."
 msgstr "Potrebna je ovjera za postavljanje vremenske zone sustava."
 
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:5
+#: src/timedate/org.freedesktop.timedate1.policy:43
 msgid "Set RTC to local timezone or UTC"
 msgstr "Postavi RTC u lokalnu vremensku zonu ili UTC"
 
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:6
+#: src/timedate/org.freedesktop.timedate1.policy:44
 msgid ""
 "Authentication is required to control whether the RTC stores the local or "
 "UTC time."
 msgstr ""
 "Potrebna je ovjera za postavljanje RTC-a u lokalnu vremensku zonu ili UTC."
 
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:7
+#: src/timedate/org.freedesktop.timedate1.policy:53
 msgid "Turn network time synchronization on or off"
 msgstr "Uključi ili isključi mrežno uklađivanje vremena"
 
-#: ../src/timedate/org.freedesktop.timedate1.policy.in.h:8
+#: src/timedate/org.freedesktop.timedate1.policy:54
 msgid ""
 "Authentication is required to control whether network time synchronization "
 "shall be enabled."
@@ -542,30 +873,40 @@ msgstr ""
 "Potrebna je ovjera za uključivanje ili isključivanje mrežnog usklađivanja "
 "vremena."
 
-#: ../src/core/dbus-unit.c:428
+#: src/core/dbus-unit.c:356
 msgid "Authentication is required to start '$(unit)'."
 msgstr "Potrebna je ovjera za pokretanje '$(unit)'."
 
-#: ../src/core/dbus-unit.c:429
+#: src/core/dbus-unit.c:357
 msgid "Authentication is required to stop '$(unit)'."
 msgstr "Potrebna je ovjera za zaustavljanje '$(unit)'."
 
-#: ../src/core/dbus-unit.c:430
+#: src/core/dbus-unit.c:358
 msgid "Authentication is required to reload '$(unit)'."
 msgstr "Potrebna je ovjera za ponovno učitavnje '$(unit)'."
 
-#: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432
+#: src/core/dbus-unit.c:359 src/core/dbus-unit.c:360
 msgid "Authentication is required to restart '$(unit)'."
 msgstr "Potrebna je ovjera za ponovno pokretanje'$(unit)'."
 
-#: ../src/core/dbus-unit.c:535
-msgid "Authentication is required to kill '$(unit)'."
-msgstr "Potrebna je ovjera za ubijanje '$(unit)'."
+#: src/core/dbus-unit.c:532
+msgid ""
+"Authentication is required to send a UNIX signal to the processes of "
+"'$(unit)'."
+msgstr "Potrebna je ovjera za slanje UNIX signala u procese '$(unit)'."
 
-#: ../src/core/dbus-unit.c:565
+#: src/core/dbus-unit.c:563
 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
 msgstr "Potrebna je ovjera za vraćanje \"neuspjelog\" stanja '$(unit)'."
 
-#: ../src/core/dbus-unit.c:597
+#: src/core/dbus-unit.c:596
 msgid "Authentication is required to set properties on '$(unit)'."
 msgstr "Potrebna je ovjera za postavljanje svojstava na '$(unit)'."
+
+#: src/core/dbus-unit.c:705
+msgid ""
+"Authentication is required to delete files and directories associated with "
+"'$(unit)'."
+msgstr ""
+"Potrebna je ovjera za za brisanje datoteka i direktorija pridruženih sa "
+"'$(unit)'."
index 98090bab601d4fec4ca63760049af2f9be342431..c9c8bc786faf6f3fa6eaeb34c36df14d3fa807c4 100644 (file)
--- a/po/hu.po
+++ b/po/hu.po
@@ -267,7 +267,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Eszközök csatolásának engedélyezése munkaállomásokhoz"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:24
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr ""
 "Hitelesítés szükséges eszköz csatolásának engedélyezéséhez egy "
 "munkaállomáshoz"
@@ -278,7 +278,7 @@ msgstr "Eszközök és munkaállomások csatolásainak törlése"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:26
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Hitelesítés szükséges az eszközök munkaállomásokhoz csatolásainak "
 "alaphelyzetbe állításához."
@@ -288,7 +288,7 @@ msgid "Power off the system"
 msgstr "A rendszer kikapcsolása"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:28
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Hitelesítés szükséges a rendszer kikapcsolásához."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:29
@@ -298,21 +298,21 @@ msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:30
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Hitelesítés szükséges a rendszer kikapcsolásához miközben be vannak "
 "jelentkezve más felhasználók."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:31
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr ""
 "A rendszer kikapcsolása miközben egy alkalmazás ennek meggátlását kérte"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:32
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 "Hitelesítés szükséges a rendszer kikapcsolásához miközben egy alkalmazás "
 "ennek meggátlását kérte."
@@ -322,7 +322,7 @@ msgid "Reboot the system"
 msgstr "A rendszer újraindítása"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:34
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Hitelesítés szükséges a rendszer újraindításához."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:35
@@ -331,21 +331,21 @@ msgstr "A rendszer újraindítása mialatt be vannak jelentkezve más felhaszná
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:36
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "Hitelesítés szükséges a rendszer újraindításához miközben be vannak "
 "jelentkezve más felhasználók."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:37
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr ""
 "A rendszer újraindítása miközben egy alkalmazás ennek meggátlását kérte"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:38
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 "Hitelesítés szükséges a rendszer újraindításához miközben egy alkalmazás "
 "ennek meggátlását kérte."
@@ -355,7 +355,7 @@ msgid "Suspend the system"
 msgstr "A rendszer felfüggesztése"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:40
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Hitelesítés szükséges a rendszer felfüggesztéséhez."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:41
@@ -365,21 +365,21 @@ msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:42
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Hitelesítés szükséges a rendszer felfüggesztéséhez miközben be vannak "
 "jelentkezve más felhasználók."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:43
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr ""
 "A rendszer felfüggesztése miközben egy alkalmazás ennek meggátlását kérte"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:44
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 "Hitelesítés szükséges a rendszer felfüggesztéséhez miközben egy alkalmazás "
 "ennek meggátlását kérte."
@@ -389,7 +389,7 @@ msgid "Hibernate the system"
 msgstr "A rendszer hibernálása"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:46
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "Hitelesítés szükséges a rendszer hibernálásához."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:47
@@ -398,20 +398,20 @@ msgstr "A rendszer hibernálása mialatt be vannak jelentkezve más felhasznál
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:48
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Hitelesítés szükséges a rendszer hibernálásához miközben be vannak "
 "jelentkezve más felhasználók."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:49
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr "A rendszer hibernálása miközben egy alkalmazás ennek meggátlását kérte"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:50
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 "Hitelesítés szükséges a rendszer hibernálásához miközben egy alkalmazás "
 "ennek meggátlását kérte."
@@ -422,7 +422,7 @@ msgstr "Aktív munkamenetek, felhasználók és munkaállomások kezelése"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:52
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Hitelesítés szükséges az aktív munkamenetek, felhasználók és munkaállomások "
 "kezeléséhez."
index 2e06e3bff0db0301df6f7f7b0c9fa91f3f098764..66dd11ec77699d32cbfd7939a11f20d112e2e1e3 100644 (file)
--- a/po/id.po
+++ b/po/id.po
@@ -252,7 +252,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Ijinkan mencantolkan perangkat ke seat"
 
 #: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr "Otentikasi diperlukan untuk mencantol suatu perangkat ke sebuah seat."
 
 #: src/login/org.freedesktop.login1.policy:148
@@ -261,7 +261,7 @@ msgstr "Siram perangkat untuk mendudukkan lampiran"
 
 #: src/login/org.freedesktop.login1.policy:149
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Otentikasi diperlukan untuk me-reset bagaimana perangkat dicantolkan ke seat."
 
@@ -270,7 +270,7 @@ msgid "Power off the system"
 msgstr "Matikan daya sistem"
 
 #: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Otentikasi diperlukan untuk mematikan daya sistem."
 
 #: src/login/org.freedesktop.login1.policy:169
@@ -279,20 +279,20 @@ msgstr "Matikan daya sistem ketika pengguna lain sedang log masuk"
 
 #: src/login/org.freedesktop.login1.policy:170
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Otentikasi diperlukan untuk mematikan daya sistem ketika pengguna lain "
 "sedang log masuk."
 
 #: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "Matikan daya sistem ketika sebuah aplikasi meminta untuk mencegahnya"
 
 #: src/login/org.freedesktop.login1.policy:181
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 "Otentikasi diperlukan untuk mematikan daya sistem ketika sebuah aplikasi "
 "meminta untuk mencegahnya."
@@ -302,7 +302,7 @@ msgid "Reboot the system"
 msgstr "Boot ulang sistem"
 
 #: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Otentikasi diperlukan untuk mem-boot ulang sistem."
 
 #: src/login/org.freedesktop.login1.policy:202
@@ -311,20 +311,20 @@ msgstr "Boot ulang sistem ketika pengguna lain sedang log masuk"
 
 #: src/login/org.freedesktop.login1.policy:203
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "Otentikasi diperlukan untuk mem-boot ulang sistem ketika pengguna lain "
 "sedang log masuk."
 
 #: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr "Boot ulang sistem ketika sebuah aplikasi meminta untuk mencegahnya"
 
 #: src/login/org.freedesktop.login1.policy:214
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 "Otentikasi diperlukan untuk mem-boot ulang sistem ketika sebuah aplikasi "
 "meminta untuk mencegahnya."
@@ -334,7 +334,7 @@ msgid "Halt the system"
 msgstr "Halt sistem"
 
 #: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
 msgstr "Otentikasi diperlukan untuk meng-halt sistem."
 
 #: src/login/org.freedesktop.login1.policy:235
@@ -343,19 +343,19 @@ msgstr "Halt sistem ketika pengguna lain sedang log masuk"
 
 #: src/login/org.freedesktop.login1.policy:236
 msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt the system while other users are "
 "logged in."
 msgstr ""
 "Otentikasi diperlukan untuk meng-halt sistem ketika pengguna lain sedang log "
 "masuk."
 
 #: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
 msgstr "Halt sistem ketika sebuah aplikasi meminta untuk mencegahnya"
 
 #: src/login/org.freedesktop.login1.policy:247
 msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
 "to inhibit it."
 msgstr ""
 "Otentikasi diperlukan untuk meng-halt sistem ketika sebuah aplikasi meminta "
@@ -366,7 +366,7 @@ msgid "Suspend the system"
 msgstr "Suspensikan sistem"
 
 #: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Otentikasi diperlukan untuk mensuspensi sistem."
 
 #: src/login/org.freedesktop.login1.policy:267
@@ -375,20 +375,20 @@ msgstr "Suspensikan sistem ketika pengguna lain sedang log masuk"
 
 #: src/login/org.freedesktop.login1.policy:268
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Otentikasi diperlukan untuk mensuspensi sistem ketika pengguna lain sedang "
 "log masuk."
 
 #: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr "Suspensikan sistem ketika sebuah aplikasi meminta untuk mencegahnya"
 
 #: src/login/org.freedesktop.login1.policy:279
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 "Otentikasi diperlukan untuk mensuspensi sistem ketika suatu aplikasi meminta "
 "untuk mencegahnya."
@@ -398,7 +398,7 @@ msgid "Hibernate the system"
 msgstr "Hibernasikan sistem"
 
 #: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "Otentikasi diperlukan untuk menghibernasi sistem."
 
 #: src/login/org.freedesktop.login1.policy:299
@@ -407,20 +407,20 @@ msgstr "Hibernasikan sistem ketika pengguna lain sedang log masuk."
 
 #: src/login/org.freedesktop.login1.policy:300
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Otentikasi diperlukan untuk menghibernasi sistem ketika pengguna lain sedang "
 "log masuk."
 
 #: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr "Hibernasikan sistem ketika sebuah aplikasi meminta untuk mencegahnya."
 
 #: src/login/org.freedesktop.login1.policy:311
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 "Otentikasi diperlukan untuk menghibernasi sistem ketika sebuah aplikasi "
 "meminta mencegahnya."
@@ -431,7 +431,7 @@ msgstr "Kelola seat, pengguna, dan sesi aktif"
 
 #: src/login/org.freedesktop.login1.policy:322
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr "Otentikasi diperlukan untuk mengelola seat, pengguna, dan sesi aktif."
 
 #: src/login/org.freedesktop.login1.policy:331
index 1eafe5a6586275d3d2be6f7d778bef075a4a5e2c..1410586999b2c95c567563db3873337d9ceea924 100644 (file)
--- a/po/it.po
+++ b/po/it.po
@@ -2,14 +2,14 @@
 #
 # Italian translation for systemd package
 # Traduzione in italiano per il pacchetto systemd
-# Daniele Medri <dmedri@gmail.com>, 2013-2019.
+# Daniele Medri <dmedri@gmail.com>, 2013-2020.
 #
 msgid ""
 msgstr ""
 "Project-Id-Version: systemd\n"
-"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
-"POT-Creation-Date: 2019-05-05 17:02+0200\n"
-"PO-Revision-Date: 2019-05-05 17:13+0200\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-02-27 17:10+0100\n"
+"PO-Revision-Date: 2020-02-27 17:22+0100\n"
 "Last-Translator: Daniele Medri <dmedri@gmail.com>\n"
 "Language-Team: Italian\n"
 "Language: it\n"
@@ -70,6 +70,59 @@ msgstr "Ricarica lo stato di systemd"
 msgid "Authentication is required to reload the systemd state."
 msgstr "Autenticazione richiesta per riavviare lo stato di sistemd."
 
+#: src/home/org.freedesktop.home1.policy:13
+msgid "Create a home area"
+msgstr "Crea un'area home"
+
+#: src/home/org.freedesktop.home1.policy:14
+msgid "Authentication is required to creat a user's home area."
+msgstr "Autenticazione richiesta per creare un'area home per l'utente."
+
+#: src/home/org.freedesktop.home1.policy:23
+msgid "Remove a home area"
+msgstr "Rimuovi un'area home"
+
+#: src/home/org.freedesktop.home1.policy:24
+msgid "Authentication is required to remov a user's home area."
+msgstr "Autenticazione richiesta per rimuovere un'area home per l'utente."
+
+#: src/home/org.freedesktop.home1.policy:33
+msgid "Check credentials of a home area"
+msgstr "Controlla credenziali di un'area home"
+
+#: src/home/org.freedesktop.home1.policy:34
+msgid ""
+"Authentication is required to check credentials against a user's home area."
+msgstr ""
+"Autenticazione richiesta per controllare le credenziali di un'area home per "
+"l'utente."
+
+#: src/home/org.freedesktop.home1.policy:43
+msgid "Update a home area"
+msgstr "Aggiorna un'area home"
+
+#: src/home/org.freedesktop.home1.policy:44
+msgid "Authentication is required to updat a user's home area."
+msgstr "Autenticazione richiesta per aggiornare un'area home per l'utente."
+
+#: src/home/org.freedesktop.home1.policy:53
+msgid "Resize a home area"
+msgstr "Ridimensiona un'area home"
+
+#: src/home/org.freedesktop.home1.policy:54
+msgid "Authentication is required to resiz a user's home area."
+msgstr "Autenticazione richiesta per ridimensionare l'area home dell'utente."
+
+#: src/home/org.freedesktop.home1.policy:63
+msgid "Change password of a home area"
+msgstr "Cambia password di un'area home"
+
+#: src/home/org.freedesktop.home1.policy:64
+msgid "Authentication is required to chang the password of a user's home area."
+msgstr ""
+"Autenticazione richiesta per cambiare le password per l'area home "
+"dell'utente."
+
 #: src/hostname/org.freedesktop.hostname1.policy:20
 msgid "Set host name"
 msgstr "Configura il nome host"
@@ -168,8 +221,8 @@ msgstr "Consenti alle applicazioni di ritardare lo spegnimento del sistema"
 #: src/login/org.freedesktop.login1.policy:34
 msgid "Authentication is required for an application to delay system shutdown."
 msgstr ""
-"Autenticazione richiesta ad un'applicazione per ritardare lo spegnimento "
-"del sistema."
+"Autenticazione richiesta ad un'applicazione per ritardare lo spegnimento del "
+"sistema."
 
 #: src/login/org.freedesktop.login1.policy:44
 msgid "Allow applications to inhibit system sleep"
@@ -206,8 +259,8 @@ msgstr ""
 #: src/login/org.freedesktop.login1.policy:75
 msgid "Allow applications to inhibit system handling of the power key"
 msgstr ""
-"Consenti alle applicazioni di inibire la gestione di sistema del tasto"
-"accensione"
+"Consenti alle applicazioni di inibire la gestione di sistema del "
+"tastoaccensione"
 
 #: src/login/org.freedesktop.login1.policy:76
 msgid ""
@@ -284,7 +337,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Consenti di collegare dispositivi alle postazioni"
 
 #: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr ""
 "Autenticazione richiesta per collegare un dispositivo ad una postazione."
 
@@ -293,8 +346,7 @@ msgid "Flush device to seat attachments"
 msgstr "Scollega i dispositivi dalla postazione"
 
 #: src/login/org.freedesktop.login1.policy:149
-msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+msgid "Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Autenticazione richiesta per ripristinare come i dispositivi sono collegati "
 "alle postazioni."
@@ -304,7 +356,7 @@ msgid "Power off the system"
 msgstr "Spegni il sistema"
 
 #: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Autenticazione richiesta per spegnere il sistema."
 
 #: src/login/org.freedesktop.login1.policy:169
@@ -313,20 +365,20 @@ msgstr "Spegni il sistema mentre altri utenti sono connessi"
 
 #: src/login/org.freedesktop.login1.policy:170
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Autenticazione richiesta per spegnere il sistema mentre altri utenti sono "
 "connessi."
 
 #: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "Spegni il sistema mentre un'applicazione chiede di inibirne l'azione"
 
 #: src/login/org.freedesktop.login1.policy:181
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application is "
+"inhibiting this."
 msgstr ""
 "Autenticazione richiesta per spegnere il sistema mentre un'applicazione "
 "chiede di inibirne l'azione."
@@ -336,7 +388,7 @@ msgid "Reboot the system"
 msgstr "Riavvia il sistema"
 
 #: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Autenticazione richiesta per riavviare il sistema."
 
 #: src/login/org.freedesktop.login1.policy:202
@@ -345,20 +397,20 @@ msgstr "Riavvia il sistema mentre altri utenti sono connessi"
 
 #: src/login/org.freedesktop.login1.policy:203
 msgid ""
-"Authentication is required for rebooting the system while other users are "
-"logged in."
+"Authentication is required to reboot the system while other users are logged "
+"in."
 msgstr ""
 "Autenticazione richiesta per riavviare il sistema mentre altri utenti sono "
 "connessi."
 
 #: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr "Riavvia il sistema mentre un'applicazione chiede di inibirne l'azione"
 
 #: src/login/org.freedesktop.login1.policy:214
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application is "
+"inhibiting this."
 msgstr ""
 "Autenticazione richiesta per riavviare il sistema mentre un'applicazione "
 "chiede di inibirne l'azione."
@@ -368,7 +420,7 @@ msgid "Halt the system"
 msgstr "Ferma il sistema"
 
 #: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
 msgstr "Autenticazione richiesta per fermare il sistema."
 
 #: src/login/org.freedesktop.login1.policy:235
@@ -377,30 +429,30 @@ msgstr "Ferma il sistema mentre altri utenti sono connessi"
 
 #: src/login/org.freedesktop.login1.policy:236
 msgid ""
-"Authentication is required for halting the system while other users are "
-"logged in."
+"Authentication is required to halt the system while other users are logged "
+"in."
 msgstr ""
 "Autenticazione richiesta per fermare il sistema mentre altri utenti sono "
 "connessi."
 
 #: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
 msgstr "Ferma il sistema mentre un'applicazione chiede di inibirne l'azione"
 
 #: src/login/org.freedesktop.login1.policy:247
 msgid ""
-"Authentication is required for halting the system while an application asked "
-"to inhibit it."
+"Authentication is required to halt the system while an application is "
+"inhibiting this."
 msgstr ""
-"Autenticazione richiesta per fermare il sistema mentre un'applicazione "
-"chiede di inibirne l'azione."
+"Autenticazione richiesta per ibernare il sistema mentre un'applicazione ne "
+"inibisce l'azione."
 
 #: src/login/org.freedesktop.login1.policy:257
 msgid "Suspend the system"
 msgstr "Sospendi il sistema"
 
 #: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Autenticazione richiesta per sospendere il sistema."
 
 #: src/login/org.freedesktop.login1.policy:267
@@ -409,20 +461,20 @@ msgstr "Sospendi il sistema mentre altri utenti sono connessi"
 
 #: src/login/org.freedesktop.login1.policy:268
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Autenticazione richiesta per sospendere il sistema mentre altri utenti sono "
 "connessi."
 
 #: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr "Sospendi il sistema mentre un'applicazione chiede di inibirne l'azione"
 
 #: src/login/org.freedesktop.login1.policy:279
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application is "
+"inhibiting this."
 msgstr ""
 "Autenticazione richiesta per sospendere il sistema mentre un'applicazione "
 "chiede di inibirne l'azione."
@@ -432,7 +484,7 @@ msgid "Hibernate the system"
 msgstr "Iberna il sistema"
 
 #: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "Autenticazione richiesta per ibernare il sistema."
 
 #: src/login/org.freedesktop.login1.policy:299
@@ -441,20 +493,20 @@ msgstr "Iberna il sistema mentre altri utenti sono connessi"
 
 #: src/login/org.freedesktop.login1.policy:300
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Autenticazione richiesta per ibernare il sistema mentre altri utenti sono "
 "connessi."
 
 #: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr "Iberna il sistema mentre un'applicazione chiede di inibirne l'azione"
 
 #: src/login/org.freedesktop.login1.policy:311
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application is "
+"inhibiting this."
 msgstr ""
 "Autenticazione richiesta per ibernare il sistema mentre un'applicazione "
 "chiede di inibirne l'azione."
@@ -464,8 +516,7 @@ msgid "Manage active sessions, users and seats"
 msgstr "Gestione delle sessioni attive, utenti e postazioni"
 
 #: src/login/org.freedesktop.login1.policy:322
-msgid ""
-"Authentication is required for managing active sessions, users and seats."
+msgid "Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Autenticazione richiesta per gestire le sessioni attive, gli utenti e le "
 "postazioni."
@@ -532,6 +583,14 @@ msgstr "Configura un messaggio per gli utenti"
 msgid "Authentication is required to set a wall message"
 msgstr "Autenticazione richiesta per configurare un messaggio per gli utenti"
 
+#: src/login/org.freedesktop.login1.policy:395
+msgid "Change Session"
+msgstr "Cambia sessione"
+
+#: src/login/org.freedesktop.login1.policy:396
+msgid "Authentication is required to change the virtual terminal."
+msgstr "Autenticazione richiesta per cambiare il terminale virtuale."
+
 #: src/machine/org.freedesktop.machine1.policy:22
 msgid "Log into a local container"
 msgstr "Accedi ad un container locale"
@@ -604,6 +663,137 @@ msgstr ""
 "Autenticazione richiesta per gestire le immagini delle virtual machine e dei "
 "container locali."
 
+#: src/network/org.freedesktop.network1.policy:22
+msgid "Set NTP servers"
+msgstr "Configura server NTP"
+
+#: src/network/org.freedesktop.network1.policy:23
+msgid "Authentication is required to set NTP servers."
+msgstr "Autenticazione richiesta per configurare i server NTP."
+
+#: src/network/org.freedesktop.network1.policy:33
+#: src/resolve/org.freedesktop.resolve1.policy:44
+msgid "Set DNS servers"
+msgstr "Configura i server DNS"
+
+#: src/network/org.freedesktop.network1.policy:34
+#: src/resolve/org.freedesktop.resolve1.policy:45
+msgid "Authentication is required to set DNS servers."
+msgstr "Autenticazione richiesta per configurare i server DNS."
+
+#: src/network/org.freedesktop.network1.policy:44
+#: src/resolve/org.freedesktop.resolve1.policy:55
+msgid "Set domains"
+msgstr "Configura domini"
+
+#: src/network/org.freedesktop.network1.policy:45
+#: src/resolve/org.freedesktop.resolve1.policy:56
+msgid "Authentication is required to set domains."
+msgstr "Autenticazione richiesta per configurare i domini."
+
+#: src/network/org.freedesktop.network1.policy:55
+#: src/resolve/org.freedesktop.resolve1.policy:66
+msgid "Set default route"
+msgstr "Configura la tabella di instradamento"
+
+#: src/network/org.freedesktop.network1.policy:56
+#: src/resolve/org.freedesktop.resolve1.policy:67
+msgid "Authentication is required to set default route."
+msgstr ""
+"Autenticazione richiesta per configurare la tabella di instradamento "
+"predefinita."
+
+#: src/network/org.freedesktop.network1.policy:66
+#: src/resolve/org.freedesktop.resolve1.policy:77
+msgid "Enable/disable LLMNR"
+msgstr "Abilita/disabilita LLMNR"
+
+#: src/network/org.freedesktop.network1.policy:67
+#: src/resolve/org.freedesktop.resolve1.policy:78
+msgid "Authentication is required to enable or disable LLMNR."
+msgstr "Autenticazione richiesta per attivare/disattivare LLMNR."
+
+#: src/network/org.freedesktop.network1.policy:77
+#: src/resolve/org.freedesktop.resolve1.policy:88
+msgid "Enable/disable multicast DNS"
+msgstr "Abilita/disabilita DNS multicast"
+
+#: src/network/org.freedesktop.network1.policy:78
+#: src/resolve/org.freedesktop.resolve1.policy:89
+msgid "Authentication is required to enable or disable multicast DNS."
+msgstr "Autenticazione richiesta per abilitare/disabilitare DNS multicast."
+
+#: src/network/org.freedesktop.network1.policy:88
+#: src/resolve/org.freedesktop.resolve1.policy:99
+msgid "Enable/disable DNS over TLS"
+msgstr "Abilita/disabilita DNS su TLS"
+
+#: src/network/org.freedesktop.network1.policy:89
+#: src/resolve/org.freedesktop.resolve1.policy:100
+msgid "Authentication is required to enable or disable DNS over TLS."
+msgstr "Autenticazione richiesta per abilitare o disabilitare DNS su TLS."
+
+#: src/network/org.freedesktop.network1.policy:99
+#: src/resolve/org.freedesktop.resolve1.policy:110
+msgid "Enable/disable DNSSEC"
+msgstr "Abilita/disabilita DNSSEC"
+
+#: src/network/org.freedesktop.network1.policy:100
+#: src/resolve/org.freedesktop.resolve1.policy:111
+msgid "Authentication is required to enable or disable DNSSEC."
+msgstr "Autenticazione richiesta per abilitare o disabilitare DNSSEC."
+
+#: src/network/org.freedesktop.network1.policy:110
+#: src/resolve/org.freedesktop.resolve1.policy:121
+msgid "Set DNSSEC Negative Trust Anchors"
+msgstr "Configura DNSSEC Negative Trust Anchors"
+
+#: src/network/org.freedesktop.network1.policy:111
+#: src/resolve/org.freedesktop.resolve1.policy:122
+msgid "Authentication is required to set DNSSEC Negative Trust Anchors."
+msgstr ""
+"Autenticazione richiesta per configurare DNSSEC Negative Trust Anchors."
+
+#: src/network/org.freedesktop.network1.policy:121
+msgid "Revert NTP settings"
+msgstr "Ripristina configurazioni NTP"
+
+#: src/network/org.freedesktop.network1.policy:122
+msgid "Authentication is required to reset NTP settings."
+msgstr "Autenticazione richiesta per ripristinare le configurazioni NTP."
+
+#: src/network/org.freedesktop.network1.policy:132
+msgid "Revert DNS settings"
+msgstr "Ripristina configurazioni DNS"
+
+#: src/network/org.freedesktop.network1.policy:133
+msgid "Authentication is required to reset DNS settings."
+msgstr "Autenticazione richiesta per ripristinare le configurazioni DNS."
+
+#: src/network/org.freedesktop.network1.policy:143
+msgid "Renew dynamic addresses"
+msgstr "Rinnova indirizzi dinamici"
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to renew dynamic addresses."
+msgstr "Autenticazione richiesta per rinnovare gli indirizzi dinamici."
+
+#: src/network/org.freedesktop.network1.policy:154
+msgid "Reload network settings"
+msgstr "Ricarica configurazioni di rete"
+
+#: src/network/org.freedesktop.network1.policy:155
+msgid "Authentication is required to reload network settings."
+msgstr "Autenticazione richiesta per ricaricare le configurazioni di rete."
+
+#: src/network/org.freedesktop.network1.policy:165
+msgid "Reconfigure network interface"
+msgstr "Riconfigura interfaccia di rete"
+
+#: src/network/org.freedesktop.network1.policy:166
+msgid "Authentication is required to reconfigure network interface."
+msgstr "Autenticazione richiesta per riconfigurare l'interfaccia di rete."
+
 #: src/portable/org.freedesktop.portable1.policy:13
 msgid "Inspect a portable service image"
 msgstr "Ispeziona un'immagine di servizio portabile"
@@ -652,6 +842,16 @@ msgid "Authentication is required to unregister a DNS-SD service"
 msgstr ""
 "Autenticazione richiesta per annullare la registrazione di un servizio DNS-SD"
 
+#: src/resolve/org.freedesktop.resolve1.policy:132
+msgid "Revert name resolution settings"
+msgstr "Ripristina le configurazioni per la risoluzione dei nomi"
+
+#: src/resolve/org.freedesktop.resolve1.policy:133
+msgid "Authentication is required to reset name resolution settings."
+msgstr ""
+"Autenticazione richiesta per ripristinare le configurazioni per la "
+"risoluzione dei nomi."
+
 #: src/timedate/org.freedesktop.timedate1.policy:22
 msgid "Set system time"
 msgstr "Configura l'orario di sistema"
@@ -694,23 +894,23 @@ msgstr ""
 "Autenticazione richiesta per verificare se la sincronizzazione dell'orario "
 "in rete deve essere attivata."
 
-#: src/core/dbus-unit.c:317
+#: src/core/dbus-unit.c:356
 msgid "Authentication is required to start '$(unit)'."
 msgstr "Autenticazione richiesta per avviare '$(unit)'."
 
-#: src/core/dbus-unit.c:318
+#: src/core/dbus-unit.c:357
 msgid "Authentication is required to stop '$(unit)'."
 msgstr "Autenticazione richiesta per fermare '$(unit)'."
 
-#: src/core/dbus-unit.c:319
+#: src/core/dbus-unit.c:358
 msgid "Authentication is required to reload '$(unit)'."
 msgstr "Autenticazione richiesta per ricaricare '$(unit)'."
 
-#: src/core/dbus-unit.c:320 src/core/dbus-unit.c:321
+#: src/core/dbus-unit.c:359 src/core/dbus-unit.c:360
 msgid "Authentication is required to restart '$(unit)'."
 msgstr "Autenticazione richiesta per riavviare '$(unit)'."
 
-#: src/core/dbus-unit.c:493
+#: src/core/dbus-unit.c:532
 msgid ""
 "Authentication is required to send a UNIX signal to the processes of "
 "'$(unit)'."
@@ -718,11 +918,19 @@ msgstr ""
 "Autenticazione richiesta per inviare un segnale UNIX ai processi di "
 "'$(unit)'."
 
-#: src/core/dbus-unit.c:524
+#: src/core/dbus-unit.c:563
 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
 msgstr ""
 "Autenticazione richiesta per riconfigurare lo stato \"fallito\" di '$(unit)'."
 
-#: src/core/dbus-unit.c:557
+#: src/core/dbus-unit.c:596
 msgid "Authentication is required to set properties on '$(unit)'."
 msgstr "Autenticazione richiesta per configurare le proprietà di '$(unit)'."
+
+#: src/core/dbus-unit.c:705
+msgid ""
+"Authentication is required to delete files and directories associated with "
+"'$(unit)'."
+msgstr ""
+"Autenticazione richiesta per eliminare i file e le directory associate a "
+"'$(unit)'."
index 55106f4b01839702dd43bc37ec24acaf9a5de7c2..0847af1f71080f9a6e2dd8fbb8029e4d5ca05685 100644 (file)
--- a/po/ja.po
+++ b/po/ja.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: systemd\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-09-21 20:13+0900\n"
+"POT-Creation-Date: 2020-02-02 23:20+0900\n"
 "PO-Revision-Date: 2018-10-27 07:41+0900\n"
 "Last-Translator: Yu Watanabe <watanabe.yu+github@gmail.com>\n"
 "Language-Team: \n"
@@ -60,6 +60,55 @@ msgstr "systemdの状態の再読込"
 msgid "Authentication is required to reload the systemd state."
 msgstr "systemdの状態を再読込するには認証が必要です。"
 
+#: src/home/org.freedesktop.home1.policy:13
+msgid "Create a home area"
+msgstr "ホーム領域の作成"
+
+#: src/home/org.freedesktop.home1.policy:14
+msgid "Authentication is required to create a user's home area."
+msgstr "ユーザのホーム領域を作成するには認証が必要です。"
+
+#: src/home/org.freedesktop.home1.policy:23
+msgid "Remove a home area"
+msgstr "ホーム領域の削除"
+
+#: src/home/org.freedesktop.home1.policy:24
+msgid "Authentication is required to remove a user's home area."
+msgstr "ユーザのホーム領域の削除には認証が必要です。"
+
+#: src/home/org.freedesktop.home1.policy:33
+msgid "Check credentials of a home area"
+msgstr "ホーム領域の認証情報の確認"
+
+#: src/home/org.freedesktop.home1.policy:34
+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"
+msgstr "ホーム領域の更新"
+
+#: src/home/org.freedesktop.home1.policy:44
+msgid "Authentication is required to update a user's home area."
+msgstr "ユーザのホーム領域の更新には認証が必要です。"
+
+#: src/home/org.freedesktop.home1.policy:53
+msgid "Resize a home area"
+msgstr "ホーム領域のサイズ変更"
+
+#: src/home/org.freedesktop.home1.policy:54
+msgid "Authentication is required to resize a user's home area."
+msgstr "ユーザのホーム領域のサイズ変更には認証が必要です。"
+
+#: src/home/org.freedesktop.home1.policy:63
+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."
+msgstr "ユーザのホーム領域のパスワードを変更するには認証が必要です。"
+
 #: src/hostname/org.freedesktop.hostname1.policy:20
 msgid "Set host name"
 msgstr "ホスト名の設定"
@@ -245,7 +294,7 @@ msgid "Allow attaching devices to seats"
 msgstr "シートにデバイスを接続することを許可"
 
 #: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr "シートにデバイスを接続するには認証が必要です。"
 
 #: src/login/org.freedesktop.login1.policy:148
@@ -254,7 +303,7 @@ msgstr "デバイスのシートへの接続のリセット"
 
 #: src/login/org.freedesktop.login1.policy:149
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr "デバイスのシートへの接続をリセットするには認証が必要です。"
 
 #: src/login/org.freedesktop.login1.policy:158
@@ -262,7 +311,7 @@ msgid "Power off the system"
 msgstr "システムの電源を切る"
 
 #: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "システムの電源を切るには認証が必要です。"
 
 #: src/login/org.freedesktop.login1.policy:169
@@ -271,19 +320,19 @@ msgstr "他のユーザがログインしている状態でシステムの電源
 
 #: src/login/org.freedesktop.login1.policy:170
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "他のユーザがログインしている状態でシステムの電源を切るには認証が必要です。"
 
 #: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "アプリケーションが使用されている状態でシステムの電源を切る"
 
 #: src/login/org.freedesktop.login1.policy:181
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 "アプリケーションが使用されている状態でシステムの電源を切るには認証が必要で"
 "す。"
@@ -293,7 +342,7 @@ msgid "Reboot the system"
 msgstr "システムの再起動"
 
 #: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "システムの再起動には認証が必要です。"
 
 #: src/login/org.freedesktop.login1.policy:202
@@ -302,19 +351,19 @@ msgstr "他のユーザがログインしている状態でシステムを再起
 
 #: src/login/org.freedesktop.login1.policy:203
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "他のユーザがログインしている状態でシステムを再起動するには認証が必要です。"
 
 #: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr "アプリケーションが使用されている状態でシステムを再起動する"
 
 #: src/login/org.freedesktop.login1.policy:214
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 "アプリケーションが使用されている状態でシステムを再起動するには認証が必要で"
 "す。"
@@ -324,7 +373,7 @@ msgid "Halt the system"
 msgstr "システムの停止"
 
 #: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
 msgstr "システムを停止するには認証が必要です。"
 
 #: src/login/org.freedesktop.login1.policy:235
@@ -333,18 +382,18 @@ msgstr "他のユーザがログインしている状態でシステムを停止
 
 #: src/login/org.freedesktop.login1.policy:236
 msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt the system while other users are "
 "logged in."
 msgstr ""
 "他のユーザがログインしている状態でシステムを停止するには認証が必要です。"
 
 #: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
 msgstr "アプリケーションが使用されている状態でシステムを停止する"
 
 #: src/login/org.freedesktop.login1.policy:247
 msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
 "to inhibit it."
 msgstr ""
 "アプリケーションが使用されている状態でシステムを停止するには認証が必要です。"
@@ -354,7 +403,7 @@ msgid "Suspend the system"
 msgstr "システムのサスペンド"
 
 #: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "システムのサスペンドには認証が必要です。"
 
 #: src/login/org.freedesktop.login1.policy:267
@@ -363,20 +412,20 @@ msgstr "他のユーザがログインしている状態でシステムをサス
 
 #: src/login/org.freedesktop.login1.policy:268
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "他のユーザがログインしている状態でシステムをサスペンドするには認証が必要で"
 "す。"
 
 #: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr "アプリケーションが使用されている状態でシステムをサスペンドする"
 
 #: src/login/org.freedesktop.login1.policy:279
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 "アプリケーションが使用されている状態でシステムをサスペンドするには認証が必要"
 "です。"
@@ -386,7 +435,7 @@ msgid "Hibernate the system"
 msgstr "システムのハイバネート"
 
 #: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "システムのハイバネートには認証が必要です。"
 
 #: src/login/org.freedesktop.login1.policy:299
@@ -395,20 +444,20 @@ msgstr "他のユーザがログインしている状態でシステムをハイ
 
 #: src/login/org.freedesktop.login1.policy:300
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "他のユーザがログインしている状態でシステムをハイバネートするには認証が必要で"
 "す。"
 
 #: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr "アプリケーションが使用されている状態でシステムをハイバネートする"
 
 #: src/login/org.freedesktop.login1.policy:311
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 "アプリケーションが使用されている状態でシステムをハイバネートするには認証が必"
 "要です。"
@@ -419,7 +468,7 @@ msgstr "アクティブなセッションやユーザ,シートの管理"
 
 #: src/login/org.freedesktop.login1.policy:322
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr "アクティブなセッションやユーザ,シートを管理するには認証が必要です。"
 
 #: src/login/org.freedesktop.login1.policy:331
@@ -481,6 +530,14 @@ msgstr "全ユーザへのメッセージの設定"
 msgid "Authentication is required to set a wall message"
 msgstr "全ユーザへのメッセージを設定するには認証が必要です。"
 
+#: src/login/org.freedesktop.login1.policy:395
+msgid "Change Session"
+msgstr "セッションの変更"
+
+#: src/login/org.freedesktop.login1.policy:396
+msgid "Authentication is required to change the virtual terminal."
+msgstr "仮想ターミナルを変更するには認証が必要です。"
+
 #: src/machine/org.freedesktop.machine1.policy:22
 msgid "Log into a local container"
 msgstr "ローカルなコンテナへログイン"
@@ -653,6 +710,30 @@ msgstr "DNSの設定を破棄"
 msgid "Authentication is required to reset DNS settings."
 msgstr "DNSの設定を破棄するには認証が必要です。"
 
+#: src/network/org.freedesktop.network1.policy:143
+msgid "Renew dynamic addresses"
+msgstr "動的アドレスの更新"
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to renew dynamic addresses."
+msgstr "動的アドレスの更新には認証が必要です。"
+
+#: src/network/org.freedesktop.network1.policy:154
+msgid "Reload network settings"
+msgstr "ネットワークの設定の再読み込み"
+
+#: src/network/org.freedesktop.network1.policy:155
+msgid "Authentication is required to reload network settings."
+msgstr "ネットワークの設定を再読み込みするには認証が必要です。"
+
+#: src/network/org.freedesktop.network1.policy:165
+msgid "Reconfigure network interface"
+msgstr "ネットワークインターフェイスの再設定"
+
+#: src/network/org.freedesktop.network1.policy:166
+msgid "Authentication is required to reconfigure network interface."
+msgstr "ネットワークインターフェイスの再設定には認証が必要です。"
+
 #: src/portable/org.freedesktop.portable1.policy:13
 msgid "Inspect a portable service image"
 msgstr "ポータブルサービスイメージの読み込み"
@@ -741,37 +822,37 @@ msgid ""
 "shall be enabled."
 msgstr "ネットワーク経由の時刻同期を有効もしくは無効にするには認証が必要です。"
 
-#: src/core/dbus-unit.c:354
+#: src/core/dbus-unit.c:355
 msgid "Authentication is required to start '$(unit)'."
 msgstr "'$(unit)'を開始するには認証が必要です。"
 
-#: src/core/dbus-unit.c:355
+#: src/core/dbus-unit.c:356
 msgid "Authentication is required to stop '$(unit)'."
 msgstr "'$(unit)'を停止するには認証が必要です。"
 
-#: src/core/dbus-unit.c:356
+#: src/core/dbus-unit.c:357
 msgid "Authentication is required to reload '$(unit)'."
 msgstr "'$(unit)'を再読込するには認証が必要です。"
 
-#: src/core/dbus-unit.c:357 src/core/dbus-unit.c:358
+#: src/core/dbus-unit.c:358 src/core/dbus-unit.c:359
 msgid "Authentication is required to restart '$(unit)'."
 msgstr "'$(unit)'を再起動するには認証が必要です。"
 
-#: src/core/dbus-unit.c:530
+#: src/core/dbus-unit.c:531
 msgid ""
 "Authentication is required to send a UNIX signal to the processes of "
 "'$(unit)'."
 msgstr "'$(unit)'のプロセスにUNIXシグナルを送るには認証が必要です。"
 
-#: src/core/dbus-unit.c:561
+#: src/core/dbus-unit.c:562
 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
 msgstr "'$(unit)'の「失敗」状態をリセットするには認証が必要です。"
 
-#: src/core/dbus-unit.c:594
+#: src/core/dbus-unit.c:595
 msgid "Authentication is required to set properties on '$(unit)'."
 msgstr "'$(unit)'のプロパティを設定するには認証が必要です。"
 
-#: src/core/dbus-unit.c:703
+#: src/core/dbus-unit.c:704
 msgid ""
 "Authentication is required to delete files and directories associated with "
 "'$(unit)'."
index e2805462d5e64250ad1619371c2dc064e4b0bd27..53899fd9efc9ed921405c31c3feb0a5935f2c035 100644 (file)
--- a/po/ko.po
+++ b/po/ko.po
@@ -238,7 +238,7 @@ msgid "Allow attaching devices to seats"
 msgstr "시트에 장치 부착 허용"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:22
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr "시트에 장치 부착을 허용하려면 인증이 필요합니다."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:23
@@ -247,7 +247,7 @@ msgstr "시트로부터 장치 해제 허용"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:24
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr "시트에 붙인 장치 상태를 초기화하려면 인증이 필요합니다."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:25
@@ -255,7 +255,7 @@ msgid "Power off the system"
 msgstr "시스템 끄기"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:26
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "시스템을 끄려면 인증이 필요합니다."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:27
@@ -264,18 +264,18 @@ msgstr "다른 사용자가 로그인 했을 때 시스템 끄기"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:28
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr "다른 사용자가 로그인 했을 때 시스템 전원을 끄려면 인증이 필요합니다."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:29
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "프로그램이 시스템을 끄지 못하게 요청할 때 시스템 전원 끄기"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:30
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 "프로그램이 시스템을 끄지 못하게 요청할 때 시스템 전원을 끄려면 인증이 필요합"
 "니다."
@@ -285,7 +285,7 @@ msgid "Reboot the system"
 msgstr "시스템 다시 시작"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:32
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "시스템을 다시 시작하려면 인증이 필요합니다."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:33
@@ -294,19 +294,19 @@ msgstr "다른 사용자가 로그인 했을 때 시스템 다시 시작"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:34
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "다른 사용자가 로그인 했을 때 시스템을 다시 시작하려면 인증이 필요합니다."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:35
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr "프로그램이 시스템을 다시 시작하지 못하게 요청할 때 시스템 다시 시작"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:36
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 "프로그램이 시스템을 다시 시작하지 못하게 요청할 때 시스템을 다시 시작하려면 "
 "인증이 필요합니다."
@@ -316,7 +316,7 @@ msgid "Suspend the system"
 msgstr "시스템 절전 상태 진입"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:38
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "시스템을 절전 상태로 놓으려면 인증이 필요합니다."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:39
@@ -325,19 +325,19 @@ msgstr "다른 사용자가 로그인 했을 때 시스템 절전 상태 진입"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:40
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "다른 사용자가 로그인 했을 때 시스템을 절전 상태로 놓으려면 인증이 필요합니다."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:41
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr "프로그램이 절전 상태 진입을 못하게 요청할 때 시스템 절전 상태 진입"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:42
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 "프로그램이 절전 상태 진입을 못하게 요청할 때 시스템을 절전 상태로 놓으려면 인"
 "증이 필요합니다."
@@ -347,7 +347,7 @@ msgid "Hibernate the system"
 msgstr "시스템 최대 절전 상태 진입"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:44
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "시스템을 최대 절전 상태로 놓으려면 인증이 필요합니다."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:45
@@ -356,21 +356,21 @@ msgstr "다른 사용자가 로그인 했을 때 시스템 최대 절전 상태
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:46
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "다른 사용자가 로그인 했을 때 시스템을 최대 절전 상태로 놓으려면 인증이 필요합"
 "니다."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:47
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr ""
 "프로그램이 최대 절전 상태 진입을 못하게 요청할 때 시스템 최대 절전 상태 진입"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:48
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 "프로그램이 최대 절전 상태 진입을 못하게 요청할 때 시스템을 최대 절전 상태로 "
 "놓으려면 인증이 필요합니다."
@@ -381,7 +381,7 @@ msgstr "활성 세션, 사용자, 시트 관리"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:50
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr "활성 세션, 사용자 시트를 관리하려면 인증이 필요합니다."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:51
index 3790f9c14db40f494949693ee009c65ccb050dee..3350b56ee169026f9f149758ab0bca49d2d49344 100644 (file)
--- a/po/lt.po
+++ b/po/lt.po
@@ -272,7 +272,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Leisti prijungti įrenginius prie darbo vietų"
 
 #: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr ""
 "Norint prijungti įrenginį prie darbo vietos, reikia nustatyti tapatybę."
 
@@ -282,7 +282,7 @@ msgstr "Išvalyti įrenginių prijungimus prie darbo vietų"
 
 #: src/login/org.freedesktop.login1.policy:149
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Norint atstatyti tai, kaip įrenginiai yra prijungti prie darbo vietų, reikia "
 "nustatyti tapatybę."
@@ -292,7 +292,7 @@ msgid "Power off the system"
 msgstr "Išjungti sistemos maitinimą"
 
 #: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Norint išjungti sistemos maitinimą, reikia nustatyti tapatybę."
 
 #: src/login/org.freedesktop.login1.policy:169
@@ -301,20 +301,20 @@ msgstr "Išjungti sistemos maitinimą nepaisant kitų prisijungusių naudotojų"
 
 #: src/login/org.freedesktop.login1.policy:170
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Norint išjungti sistemos maitinimą nepaisant kitų prisijungusių naudotojų, "
 "reikia nustatyti tapatybę."
 
 #: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "Išjungti sistemos maitinimą, nors programa paprašė tai sulaikyti"
 
 #: src/login/org.freedesktop.login1.policy:181
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 "Norint išjungti sistemos maitinimą, nepaisant to, kad programa paprašė tai "
 "sulaikyti, reikia nustatyti tapatybę."
@@ -324,7 +324,7 @@ msgid "Reboot the system"
 msgstr "Paleisti sistemą iš naujo"
 
 #: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Norint paleisti sistemą iš naujo, reikia nustatyti tapatybę."
 
 #: src/login/org.freedesktop.login1.policy:202
@@ -333,20 +333,20 @@ msgstr "Paleisti sistemą iš naujo nepaisant kitų prisijungusių naudotojų"
 
 #: src/login/org.freedesktop.login1.policy:203
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "Norint paleisti sistemą iš naujo nepaisant kitų prisijungusių naudotojų, "
 "reikia nustatyti tapatybę."
 
 #: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr "Paleisti sistemą iš naujo, nors programa paprašė tai sulaikyti"
 
 #: src/login/org.freedesktop.login1.policy:214
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 "Norint paleisti sistemą iš naujo, nepaisant to, kad programa paprašė tai "
 "sulaikyti, reikia nustatyti tapatybę."
@@ -356,7 +356,7 @@ msgid "Halt the system"
 msgstr "Stabdyti sistemą"
 
 #: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
 msgstr "Norint stabdyti sistemą, reikia nustatyti tapatybę."
 
 #: src/login/org.freedesktop.login1.policy:235
@@ -365,19 +365,19 @@ msgstr "Stabdyti sistemą nepaisant kitų prisijungusių naudotojų"
 
 #: src/login/org.freedesktop.login1.policy:236
 msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt the system while other users are "
 "logged in."
 msgstr ""
 "Norint stabdyti sistemą nepaisant kitų prisijungusių naudotojų, reikia "
 "nustatyti tapatybę."
 
 #: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
 msgstr "Stabdyti sistemą, nors programa paprašė tai sulaikyti"
 
 #: src/login/org.freedesktop.login1.policy:247
 msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
 "to inhibit it."
 msgstr ""
 "Norint stabdyti sistemą, nepaisant to, kad programa paprašė tai sulaikyti, "
@@ -388,7 +388,7 @@ msgid "Suspend the system"
 msgstr "Pristabdyti sistemą"
 
 #: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Norint pristabdyti sistemą, reikia nustatyti tapatybę."
 
 #: src/login/org.freedesktop.login1.policy:267
@@ -397,20 +397,20 @@ msgstr "Pristabdyti sistemą nepaisant kitų prisijungusių naudotojų"
 
 #: src/login/org.freedesktop.login1.policy:268
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Norint pristabdyti sistemą nepaisant kitų prisijungusių naudotojų, reikia "
 "nustatyti tapatybę."
 
 #: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr "Pristabdyti sistemą, nors programa paprašė tai sulaikyti"
 
 #: src/login/org.freedesktop.login1.policy:279
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 "Norint pristabdyti sistemą, nepaisant to, kad programa paprašė tai "
 "sulaikyti, reikia nustatyti tapatybę."
@@ -420,7 +420,7 @@ msgid "Hibernate the system"
 msgstr "Užmigdyti sistemą"
 
 #: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "Norint užmigdyti sistemą, reikia nustatyti tapatybę."
 
 #: src/login/org.freedesktop.login1.policy:299
@@ -429,20 +429,20 @@ msgstr "Užmigdyti sistemą nepaisant kitų prisijungusių naudotojų"
 
 #: src/login/org.freedesktop.login1.policy:300
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Norint užmigdyti sistemą nepaisant kitų prisijungusių naudotojų, reikia "
 "nustatyti tapatybę."
 
 #: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr "Užmigdyti sistemą, nors programa paprašė tai sulaikyti"
 
 #: src/login/org.freedesktop.login1.policy:311
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 "Norint užmigdyti sistemą, nepaisant to, kad programa paprašė tai sulaikyti, "
 "reikia nustatyti tapatybę."
@@ -453,7 +453,7 @@ msgstr "Tvarkyti aktyvius seansus, naudotojus ir darbo vietas"
 
 #: src/login/org.freedesktop.login1.policy:322
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Norint tvarkyti aktyvius seansus, naudotojus ir darbo vietas, reikia "
 "nustatyti tapatybę."
index 6441eb78dcc14320b8d106934fa09d26f029f105..4e865742139232acbf0348efb40d8e18e75e1df2 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: 2019-10-26 14:02+0000\n"
-"PO-Revision-Date: 2019-10-26 16:05+0200\n"
+"POT-Creation-Date: 2020-02-29 15:12+0000\n"
+"PO-Revision-Date: 2020-03-01 14:45+0100\n"
 "Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n"
 "Language-Team: Polish <trans-pl@lists.fedoraproject.org>\n"
 "Language: pl\n"
@@ -69,6 +69,66 @@ msgstr "Ponowne wczytanie stanu systemd"
 msgid "Authentication is required to reload the systemd state."
 msgstr "Wymagane jest uwierzytelnienie, aby ponownie wczytać stan systemd."
 
+#: src/home/org.freedesktop.home1.policy:13
+msgid "Create a home area"
+msgstr "Utworzenie przestrzeni domowej"
+
+#: src/home/org.freedesktop.home1.policy:14
+msgid "Authentication is required to create a user's home area."
+msgstr ""
+"Wymagane jest uwierzytelnienie, aby utworzyć przestrzeń domową użytkownika."
+
+#: src/home/org.freedesktop.home1.policy:23
+msgid "Remove a home area"
+msgstr "Usunięcie przestrzeni domowej"
+
+#: src/home/org.freedesktop.home1.policy:24
+msgid "Authentication is required to remove a user's home area."
+msgstr ""
+"Wymagane jest uwierzytelnienie, aby usunąć przestrzeń domową użytkownika."
+
+#: src/home/org.freedesktop.home1.policy:33
+msgid "Check credentials of a home area"
+msgstr "Sprawdzenie danych uwierzytelniających przestrzeni domowej"
+
+#: src/home/org.freedesktop.home1.policy:34
+msgid ""
+"Authentication is required to check credentials against a user's home area."
+msgstr ""
+"Wymagane jest uwierzytelnienie, aby sprawdzić dane uwierzytelniające "
+"przestrzeni domowej użytkownika."
+
+#: src/home/org.freedesktop.home1.policy:43
+msgid "Update a home area"
+msgstr "Aktualizacja przestrzeni domowej"
+
+#: src/home/org.freedesktop.home1.policy:44
+msgid "Authentication is required to update a user's home area."
+msgstr ""
+"Wymagane jest uwierzytelnienie, aby zaktualizować przestrzeń domową "
+"użytkownika."
+
+#: src/home/org.freedesktop.home1.policy:53
+msgid "Resize a home area"
+msgstr "Zmiana rozmiaru przestrzeni domowej"
+
+#: src/home/org.freedesktop.home1.policy:54
+msgid "Authentication is required to resize a user's home area."
+msgstr ""
+"Wymagane jest uwierzytelnienie, aby zmienić rozmiar przestrzeni domowej "
+"użytkownika."
+
+#: src/home/org.freedesktop.home1.policy:63
+msgid "Change password of a home area"
+msgstr "Zmiana hasła przestrzeni domowej"
+
+#: src/home/org.freedesktop.home1.policy:64
+msgid ""
+"Authentication is required to change the password of a user's home area."
+msgstr ""
+"Wymagane jest uwierzytelnienie, aby zmienić hasło przestrzeni domowej "
+"użytkownika."
+
 #: src/hostname/org.freedesktop.hostname1.policy:20
 msgid "Set host name"
 msgstr "Ustawienie nazwy komputera"
@@ -273,7 +333,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Zezwolenie na podłączanie urządzeń do stanowisk"
 
 #: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr ""
 "Wymagane jest uwierzytelnienie, aby podłączyć urządzenie do stanowiska."
 
@@ -282,8 +342,7 @@ msgid "Flush device to seat attachments"
 msgstr "Usunięcie podłączenia urządzeń do stanowisk"
 
 #: src/login/org.freedesktop.login1.policy:149
-msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+msgid "Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Wymagane jest uwierzytelnienie, aby ponownie ustawić sposób podłączenia "
 "urządzeń do stanowisk."
@@ -293,7 +352,7 @@ msgid "Power off the system"
 msgstr "Wyłączenie systemu"
 
 #: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Wymagane jest uwierzytelnienie, aby wyłączyć system."
 
 #: src/login/org.freedesktop.login1.policy:169
@@ -302,30 +361,30 @@ msgstr "Wyłączenie systemu, kiedy są zalogowani inni użytkownicy"
 
 #: src/login/org.freedesktop.login1.policy:170
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Wymagane jest uwierzytelnienie, aby wyłączyć system, kiedy są zalogowani "
 "inni użytkownicy."
 
 #: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
-msgstr "Wyłączenie systemu, kiedy program zażądał jego wstrzymania"
+msgid "Power off the system while an application is inhibiting this"
+msgstr "Wyłączenie systemu, kiedy program je wstrzymuje"
 
 #: src/login/org.freedesktop.login1.policy:181
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application is "
+"inhibiting this."
 msgstr ""
-"Wymagane jest uwierzytelnienie, aby wyłączyć system, kiedy program zażądał "
-"jego wstrzymania."
+"Wymagane jest uwierzytelnienie, aby wyłączyć system, kiedy program to "
+"wstrzymuje."
 
 #: src/login/org.freedesktop.login1.policy:191
 msgid "Reboot the system"
 msgstr "Ponowne uruchomienie systemu"
 
 #: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Wymagane jest uwierzytelnienie, aby ponownie uruchomić system."
 
 #: src/login/org.freedesktop.login1.policy:202
@@ -334,30 +393,30 @@ msgstr "Ponowne uruchomienie systemu, kiedy są zalogowani inni użytkownicy"
 
 #: src/login/org.freedesktop.login1.policy:203
 msgid ""
-"Authentication is required for rebooting the system while other users are "
-"logged in."
+"Authentication is required to reboot the system while other users are logged "
+"in."
 msgstr ""
 "Wymagane jest uwierzytelnienie, aby ponownie uruchomić system, kiedy są "
 "zalogowani inni użytkownicy."
 
 #: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
-msgstr "Ponowne uruchomienie systemu, kiedy program zażądał jego wstrzymania"
+msgid "Reboot the system while an application is inhibiting this"
+msgstr "Ponowne uruchomienie systemu, kiedy program je wstrzymuje"
 
 #: src/login/org.freedesktop.login1.policy:214
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application is "
+"inhibiting this."
 msgstr ""
 "Wymagane jest uwierzytelnienie, aby ponownie uruchomić system, kiedy program "
-"zażądał jego wstrzymania."
+"to wstrzymuje."
 
 #: src/login/org.freedesktop.login1.policy:224
 msgid "Halt the system"
 msgstr "Zatrzymanie systemu"
 
 #: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
 msgstr "Wymagane jest uwierzytelnienie, aby zatrzymać system."
 
 #: src/login/org.freedesktop.login1.policy:235
@@ -366,30 +425,30 @@ msgstr "Zatrzymanie systemu, kiedy są zalogowani inni użytkownicy"
 
 #: src/login/org.freedesktop.login1.policy:236
 msgid ""
-"Authentication is required for halting the system while other users are "
-"logged in."
+"Authentication is required to halt the system while other users are logged "
+"in."
 msgstr ""
 "Wymagane jest uwierzytelnienie, aby zatrzymać system, kiedy są zalogowani "
 "inni użytkownicy."
 
 #: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
-msgstr "Zatrzymanie systemu, kiedy program zażądał jego wstrzymania"
+msgid "Halt the system while an application is inhibiting this"
+msgstr "Zatrzymanie systemu, kiedy program je wstrzymuje"
 
 #: src/login/org.freedesktop.login1.policy:247
 msgid ""
-"Authentication is required for halting the system while an application asked "
-"to inhibit it."
+"Authentication is required to halt the system while an application is "
+"inhibiting this."
 msgstr ""
-"Wymagane jest uwierzytelnienie, aby zatrzymać system, kiedy program zażądał "
-"jego wstrzymania."
+"Wymagane jest uwierzytelnienie, aby zatrzymać system, kiedy program to "
+"wstrzymuje."
 
 #: src/login/org.freedesktop.login1.policy:257
 msgid "Suspend the system"
 msgstr "Uśpienie systemu"
 
 #: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Wymagane jest uwierzytelnienie, aby uśpić system."
 
 #: src/login/org.freedesktop.login1.policy:267
@@ -398,30 +457,30 @@ msgstr "Uśpienie systemu, kiedy są zalogowani inni użytkownicy"
 
 #: src/login/org.freedesktop.login1.policy:268
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Wymagane jest uwierzytelnienie, aby uśpić system, kiedy są zalogowani inni "
 "użytkownicy."
 
 #: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
-msgstr "Uśpienie systemu, kiedy program zażądał jego wstrzymania"
+msgid "Suspend the system while an application is inhibiting this"
+msgstr "Uśpienie systemu, kiedy program je wstrzymuje"
 
 #: src/login/org.freedesktop.login1.policy:279
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application is "
+"inhibiting this."
 msgstr ""
-"Wymagane jest uwierzytelnienie, aby uśpić system, kiedy program zażądał jego "
-"wstrzymania."
+"Wymagane jest uwierzytelnienie, aby uśpić system, kiedy program to "
+"wstrzymuje."
 
 #: src/login/org.freedesktop.login1.policy:289
 msgid "Hibernate the system"
 msgstr "Hibernacja systemu"
 
 #: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "Wymagane jest uwierzytelnienie, aby zahibernować system."
 
 #: src/login/org.freedesktop.login1.policy:299
@@ -430,31 +489,30 @@ msgstr "Hibernacja systemu, kiedy są zalogowani inni użytkownicy"
 
 #: src/login/org.freedesktop.login1.policy:300
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Wymagane jest uwierzytelnienie, aby zahibernować system, kiedy są zalogowani "
 "inni użytkownicy."
 
 #: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
-msgstr "Hibernacja systemu, kiedy program zażądał jej wstrzymania"
+msgid "Hibernate the system while an application is inhibiting this"
+msgstr "Hibernacja systemu, kiedy program ją wstrzymuje"
 
 #: src/login/org.freedesktop.login1.policy:311
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application is "
+"inhibiting this."
 msgstr ""
-"Wymagane jest uwierzytelnienie, aby zahibernować system, kiedy program "
-"zażądał jej wstrzymania."
+"Wymagane jest uwierzytelnienie, aby zahibernować system, kiedy program to "
+"wstrzymuje."
 
 #: src/login/org.freedesktop.login1.policy:321
 msgid "Manage active sessions, users and seats"
 msgstr "Zarządzanie aktywnymi sesjami, użytkownikami i stanowiskami"
 
 #: src/login/org.freedesktop.login1.policy:322
-msgid ""
-"Authentication is required for managing active sessions, users and seats."
+msgid "Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Wymagane jest uwierzytelnienie, aby zarządzać aktywnymi sesjami, "
 "użytkownikami i stanowiskami."
@@ -522,6 +580,14 @@ msgstr "Ustawienie komunikatu wall"
 msgid "Authentication is required to set a wall message"
 msgstr "Wymagane jest uwierzytelnienie, aby ustawić komunikat wall"
 
+#: src/login/org.freedesktop.login1.policy:395
+msgid "Change Session"
+msgstr "Zmiana sesji"
+
+#: src/login/org.freedesktop.login1.policy:396
+msgid "Authentication is required to change the virtual terminal."
+msgstr "Wymagane jest uwierzytelnienie, aby zmienić terminal wirtualny."
+
 #: src/machine/org.freedesktop.machine1.policy:22
 msgid "Log into a local container"
 msgstr "Logowanie do lokalnego kontenera"
@@ -830,25 +896,25 @@ msgstr ""
 "Wymagane jest uwierzytelnienie, aby kontrolować, czy włączyć synchronizację "
 "czasu przez sieć."
 
-#: src/core/dbus-unit.c:354
+#: src/core/dbus-unit.c:356
 msgid "Authentication is required to start '$(unit)'."
 msgstr "Wymagane jest uwierzytelnienie, aby uruchomić jednostkę „$(unit)”."
 
-#: src/core/dbus-unit.c:355
+#: src/core/dbus-unit.c:357
 msgid "Authentication is required to stop '$(unit)'."
 msgstr "Wymagane jest uwierzytelnienie, aby zatrzymać jednostkę „$(unit)”."
 
-#: src/core/dbus-unit.c:356
+#: src/core/dbus-unit.c:358
 msgid "Authentication is required to reload '$(unit)'."
 msgstr ""
 "Wymagane jest uwierzytelnienie, aby ponownie wczytać jednostkę „$(unit)”."
 
-#: src/core/dbus-unit.c:357 src/core/dbus-unit.c:358
+#: src/core/dbus-unit.c:359 src/core/dbus-unit.c:360
 msgid "Authentication is required to restart '$(unit)'."
 msgstr ""
 "Wymagane jest uwierzytelnienie, aby ponownie uruchomić jednostkę „$(unit)”."
 
-#: src/core/dbus-unit.c:530
+#: src/core/dbus-unit.c:532
 msgid ""
 "Authentication is required to send a UNIX signal to the processes of "
 "'$(unit)'."
@@ -856,18 +922,18 @@ msgstr ""
 "Wymagane jest uwierzytelnienie, aby wysłać sygnał uniksowy do procesów "
 "jednostki „$(unit)”."
 
-#: src/core/dbus-unit.c:561
+#: src/core/dbus-unit.c:563
 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:594
+#: src/core/dbus-unit.c:596
 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:703
+#: src/core/dbus-unit.c:705
 msgid ""
 "Authentication is required to delete files and directories associated with "
 "'$(unit)'."
index 70c1881ad392dd38b95ff4df88375278ebe10171..06706fe8fca447cfa07d5c12afbd9a8f81b858f9 100644 (file)
@@ -285,7 +285,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Permitir conectar dispositivos em estações"
 
 #: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr "É necessária autenticação para conectar um dispositivo em uma estação."
 
 #: src/login/org.freedesktop.login1.policy:148
@@ -294,7 +294,7 @@ msgstr "Liberar dispositivo para conexões da estação"
 
 #: src/login/org.freedesktop.login1.policy:149
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"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."
@@ -304,7 +304,7 @@ msgid "Power off the system"
 msgstr "Desligar o sistema"
 
 #: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "É necessária autenticação para desligar o sistema."
 
 #: src/login/org.freedesktop.login1.policy:169
@@ -313,20 +313,20 @@ msgstr "Desligar o sistema enquanto outros usuários estão conectados"
 
 #: src/login/org.freedesktop.login1.policy:170
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "É necessária autenticação para desligar o sistema enquanto outros usuários "
 "estão conectados."
 
 #: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "Desligar o sistema enquanto um aplicativo solicitou inibição"
 
 #: src/login/org.freedesktop.login1.policy:181
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"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."
@@ -336,7 +336,7 @@ msgid "Reboot the system"
 msgstr "Reiniciar o sistema"
 
 #: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "É necessária autenticação para reiniciar o sistema."
 
 #: src/login/org.freedesktop.login1.policy:202
@@ -345,20 +345,20 @@ msgstr "Reiniciar o sistema enquanto outros usuários estiverem conectados"
 
 #: src/login/org.freedesktop.login1.policy:203
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"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."
 
 #: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr "Reiniciar o sistema enquanto um aplicativo solicitou inibição"
 
 #: src/login/org.freedesktop.login1.policy:214
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"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."
@@ -368,7 +368,7 @@ msgid "Halt the system"
 msgstr "Parar o sistema"
 
 #: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
 msgstr "É necessária autenticação para parar o sistema."
 
 #: src/login/org.freedesktop.login1.policy:235
@@ -377,19 +377,19 @@ msgstr "Parar o sistema enquanto outros usuários estão logados"
 
 #: src/login/org.freedesktop.login1.policy:236
 msgid ""
-"Authentication is required for halting the system while other users are "
+"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."
 
 #: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
 msgstr "Parar o sistema enquanto um aplicativo solicitou inibição"
 
 #: src/login/org.freedesktop.login1.policy:247
 msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
 "to inhibit it."
 msgstr ""
 "É necessária autenticação para parar o sistema enquanto um aplicativo "
@@ -400,7 +400,7 @@ msgid "Suspend the system"
 msgstr "Suspender o sistema"
 
 #: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "É necessária autenticação para suspender o sistema."
 
 #: src/login/org.freedesktop.login1.policy:267
@@ -409,20 +409,20 @@ msgstr "Suspender o sistema enquanto outros usuários estiverem conectados"
 
 #: src/login/org.freedesktop.login1.policy:268
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "É necessária autenticação para suspender o sistema enquanto outros usuários "
 "estiverem conectados."
 
 #: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr "Suspender o sistema enquanto um aplicativo solicitou inibição"
 
 #: src/login/org.freedesktop.login1.policy:279
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"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."
@@ -432,7 +432,7 @@ msgid "Hibernate the system"
 msgstr "Hibernar o sistema"
 
 #: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "É necessária autenticação para hibernar o sistema."
 
 #: src/login/org.freedesktop.login1.policy:299
@@ -441,20 +441,20 @@ msgstr "Hibernar o sistema enquanto outros usuários estiverem conectados"
 
 #: src/login/org.freedesktop.login1.policy:300
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "É necessária autenticação para hibernar o sistema enquanto outros usuários "
 "estiverem conectados."
 
 #: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr "Hibernar o sistema enquanto um aplicativo solicitou inibição"
 
 #: src/login/org.freedesktop.login1.policy:311
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"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."
@@ -465,7 +465,7 @@ msgstr "Gerenciar estações, usuários e sessões ativas"
 
 #: src/login/org.freedesktop.login1.policy:322
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"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."
 
index fb54fe018c091dda2ac2800c86096f5fc2ea00d9..e22f1a110a68c222af0f7f92234baa3503af005d 100644 (file)
--- a/po/ro.po
+++ b/po/ro.po
@@ -283,7 +283,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Permite atașarea dispozitivelor la locuri"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:24
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr "Autentificarea este necesară pentru a atașa un dispozitiv la un loc."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:25
@@ -292,7 +292,7 @@ msgstr "Purjează atașamentele dispozitiv-loc"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:26
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Autentificarea este necesară pentru a restabili cum dispozitivele sunt "
 "atașate la locuri."
@@ -302,7 +302,7 @@ msgid "Power off the system"
 msgstr "Oprește sistemul"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:28
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Este necesară autentificarea pentru oprirea sistemului."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:29
@@ -311,20 +311,20 @@ msgstr "Oprește sistemul în timp ce alți utilizatori sunt autentificați"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:30
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Autentificarea este necesară pentru oprirea sistemului în timp ce alți "
 "utilizatori sunt autentificați."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:31
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "Oprește sistemul în timp ce o aplicație a cerut să împiedice asta"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:32
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 "Autentificarea este necesară pentru oprirea sistemului în timp ce o "
 "aplicație a cerut să împiedice asta."
@@ -334,7 +334,7 @@ msgid "Reboot the system"
 msgstr "Repornește sistemul"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:34
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Autentificarea este necesară pentru repornirea sistemului."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:35
@@ -343,20 +343,20 @@ msgstr "Repornește sistemul în timp ce alți utilizatori sunt autentificați"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:36
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "Autentificarea este necesară pentru repornirea sistemului în timp ce alți "
 "utilizatori autentificați."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:37
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr "Repornește sistemul în timp ce o aplicație a cerut să împiedice asta"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:38
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 "Autentificarea este necesară pentru repornirea sistemului în timp ce o "
 "aplicație a cerut să împiedice asta."
@@ -366,7 +366,7 @@ msgid "Suspend the system"
 msgstr "Suspendă sistemul"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:40
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Este necesară autentificarea pentru suspendarea sistemului."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:41
@@ -375,20 +375,20 @@ msgstr "Suspendă sistemul în timp ce alți utilizatori sunt autentificați"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:42
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Autentificarea este necesară pentru suspendarea sistemului timp ce alți "
 "utilizatori autentificați."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:43
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr "Suspendă sistemul în timp ce o aplicație a cerut să împiedice asta"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:44
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 "Autentificarea este necesară pentru suspendarea sistemului în timp ce o "
 "aplicație a cerut să împiedice asta."
@@ -398,7 +398,7 @@ msgid "Hibernate the system"
 msgstr "Hiberneaza sistemul"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:46
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "Este necesară autentificarea pentru hibernarea sistemului."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:47
@@ -407,20 +407,20 @@ msgstr "Hiberneaza sistemul în timp ce alți utilizatori sunt autentificați"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:48
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Autentificarea este necesară pentru hibernare a sistemului în timp ce alți "
 "utilizatori sunt autentificați."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:49
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr "Hibernează sistemul în timp ce o aplicație a cerut să împiedice asta"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:50
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 "Autentificarea este necesară pentru hibernarea sistemului în timp ce o "
 "aplicație a cerut să împiedice asta."
@@ -431,7 +431,7 @@ msgstr "Gestionează sesiuni active, utilizatori și locuri"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:52
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Autentificarea este necesară pentru gestionarea sesiunilor active, "
 "utilizatorilor și locurilor."
index bda6a45627004a61b88792cf3dd0af32b56788be..35da66eefb90fe4c6ecdcdda5fc249a42f1052f3 100644 (file)
--- a/po/ru.po
+++ b/po/ru.po
@@ -1,22 +1,24 @@
 # SPDX-License-Identifier: LGPL-2.1+
 #
 # translation of ru.po to Rissian
+#
 # Julia Dronova <juliette.tux@gmail.com>, 2013.
 # Sergey Ptashnick <0comffdiz@inbox.ru>, 2013-2018.
-#
+# Vladimir Yerilov <openmindead@gmail.com>, 2020.
 msgid ""
 msgstr ""
 "Project-Id-Version: systemd\n"
-"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
-"POT-Creation-Date: 2015-11-22 16:37+0100\n"
-"PO-Revision-Date: 2018-09-01 18:46+0300\n"
-"Last-Translator: Sergey Ptashnick <0comffdiz@inbox.ru>\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-03-03 00:56+1000\n"
+"PO-Revision-Date: 2020-03-03 16:05+1000\n"
+"Last-Translator: Vladimir Yerilov <openmindead@gmail.com>\n"
 "Language: ru\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
-"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<"
+"=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+"X-Generator: Lokalize 19.12.2\n"
 
 #: src/core/org.freedesktop.systemd1.policy.in:22
 msgid "Send passphrase back to system"
@@ -69,6 +71,68 @@ msgstr ""
 "Чтобы заставить systemd перечитать конфигурацию, необходимо пройти "
 "аутентификацию."
 
+#: src/home/org.freedesktop.home1.policy:13
+msgid "Create a home area"
+msgstr "Создать домашнее пространство"
+
+#: src/home/org.freedesktop.home1.policy:14
+msgid "Authentication is required to create a user's home area."
+msgstr ""
+"Чтобы создать домашнее пространство пользователя, необходимо пройти"
+" аутентификацию."
+
+#: src/home/org.freedesktop.home1.policy:23
+msgid "Remove a home area"
+msgstr "Удалить домашнее пространство"
+
+#: src/home/org.freedesktop.home1.policy:24
+msgid "Authentication is required to remove a user's home area."
+msgstr ""
+"Чтобы удалить домашнее пространство пользователя, необходимо пройти"
+" аутентификацию."
+
+#: src/home/org.freedesktop.home1.policy:33
+msgid "Check credentials of a home area"
+msgstr "Проверить учётные данные домашнего пространства"
+
+#: src/home/org.freedesktop.home1.policy:34
+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"
+msgstr "Обновить домашнее пространство"
+
+#: src/home/org.freedesktop.home1.policy:44
+msgid "Authentication is required to update a user's home area."
+msgstr ""
+"Чтобы обновить домашнее пространство пользователя, необходимо пройти"
+" аутентификацию."
+
+#: src/home/org.freedesktop.home1.policy:53
+msgid "Resize a home area"
+msgstr "Изменить размер домашнего пространства"
+
+#: src/home/org.freedesktop.home1.policy:54
+msgid "Authentication is required to resize a user's home area."
+msgstr ""
+"Чтобы изменить размер домашнего пространства пользователя, необходимо пройти"
+" аутентификацию."
+
+#: src/home/org.freedesktop.home1.policy:63
+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."
+msgstr ""
+"Чтобы изменить пароль для домашнего пространства пользователя, необходимо"
+" пройти аутентификацию."
+
 #: src/hostname/org.freedesktop.hostname1.policy:20
 msgid "Set host name"
 msgstr "Настроить имя компьютера"
@@ -266,8 +330,7 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:117
 msgid "Allow non-logged-in user to run programs"
-msgstr ""
-"Разрешить работу программ в фоновом режиме после завершения сеанса"
+msgstr "Разрешить работу программ в фоновом режиме после завершения сеанса"
 
 #: src/login/org.freedesktop.login1.policy:118
 msgid "Explicit request is required to run programs as a non-logged-in user."
@@ -292,7 +355,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Разрешить подключение устройств к рабочим местам"
 
 #: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr ""
 "Чтобы разрешить подключение устройств к рабочим местам, необходимо пройти "
 "аутентификацию."
@@ -302,8 +365,7 @@ msgid "Flush device to seat attachments"
 msgstr "Сбросить привязки устройств к рабочим местам"
 
 #: src/login/org.freedesktop.login1.policy:149
-msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+msgid "Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Чтобы сбросить привязки устройств к рабочим местам, необходимо пройти "
 "аутентификацию."
@@ -313,7 +375,7 @@ msgid "Power off the system"
 msgstr "Выключить систему"
 
 #: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Чтобы выключить систему, необходимо пройти аутентификацию."
 
 #: src/login/org.freedesktop.login1.policy:169
@@ -323,22 +385,22 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:170
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Чтобы выключить систему, несмотря на то, что в ней работают другие "
 "пользователи, необходимо пройти аутентификацию."
 
 #: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr ""
 "Выключить систему, несмотря на то, что приложение запросило блокировку "
 "выключения"
 
 #: src/login/org.freedesktop.login1.policy:181
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application is "
+"inhibiting this."
 msgstr ""
 "Чтобы выключить систему, несмотря на то, что приложение запросило блокировку "
 "выключения, необходимо пройти аутентификацию."
@@ -348,7 +410,7 @@ msgid "Reboot the system"
 msgstr "Перезагрузить систему"
 
 #: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Чтобы перезагрузить систему, необходимо пройти аутентификацию."
 
 #: src/login/org.freedesktop.login1.policy:202
@@ -358,22 +420,22 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:203
 msgid ""
-"Authentication is required for rebooting the system while other users are "
-"logged in."
+"Authentication is required to reboot the system while other users are logged "
+"in."
 msgstr ""
 "Чтобы перезагрузить систему, несмотря на то, что в ней работают другие "
 "пользователи, необходимо пройти аутентификацию."
 
 #: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr ""
 "Перезагрузить систему, несмотря на то, что приложение запросило блокировку "
 "выключения"
 
 #: src/login/org.freedesktop.login1.policy:214
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application is "
+"inhibiting this."
 msgstr ""
 "Чтобы перезагрузить систему, несмотря на то, что приложение запросило "
 "блокировку выключения, необходимо пройти аутентификацию."
@@ -383,7 +445,7 @@ msgid "Halt the system"
 msgstr "Остановить систему"
 
 #: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
 msgstr "Чтобы остановить систему, необходимо пройти аутентификацию."
 
 #: src/login/org.freedesktop.login1.policy:235
@@ -393,32 +455,32 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:236
 msgid ""
-"Authentication is required for halting the system while other users are "
-"logged in."
+"Authentication is required to halt the system while other users are logged "
+"in."
 msgstr ""
 "Чтобы остановить систему, несмотря на то, что в ней работают другие "
 "пользователи, необходимо пройти аутентификацию."
 
 #: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
 msgstr ""
-"Остановить систему, несмотря на то, что приложение запросило блокировку "
+"Остановить систему несмотря на то, что приложение запросило блокировку "
 "выключения"
 
 #: src/login/org.freedesktop.login1.policy:247
 msgid ""
-"Authentication is required for halting 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
 msgid "Suspend the system"
 msgstr "Перевести систему в ждущий режим"
 
 #: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr ""
 "Чтобы перевести систему в ждущий режим, необходимо пройти аутентификацию."
 
@@ -430,22 +492,22 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:268
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Чтобы перевести систему в ждущий режим, несмотря на то, что в ней работают "
 "другие пользователи, необходимо пройти аутентификацию."
 
 #: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr ""
 "Перевести систему в ждущий режим, несмотря на то, что приложение запросило "
 "блокировку"
 
 #: src/login/org.freedesktop.login1.policy:279
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application is "
+"inhibiting this."
 msgstr ""
 "Чтобы перевести систему в ждущий режим, несмотря на то, что приложение "
 "запросило блокировку, необходимо пройти аутентификацию."
@@ -455,7 +517,7 @@ msgid "Hibernate the system"
 msgstr "Перевести систему в спящий режим"
 
 #: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr ""
 "Чтобы перевести систему в спящий режим, необходимо пройти аутентификацию."
 
@@ -467,22 +529,22 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:300
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Чтобы перевести систему в спящий режим, несмотря на то, что в ней работают "
 "другие пользователи, необходимо пройти аутентификацию."
 
 #: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr ""
 "Перевести систему в спящий режим, несмотря на то, что приложение запросило "
 "блокировку"
 
 #: src/login/org.freedesktop.login1.policy:311
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application is "
+"inhibiting this."
 msgstr ""
 "Чтобы перевести систему в спящий режим, несмотря на то, что приложение "
 "запросило блокировку, необходимо пройти аутентификацию."
@@ -492,8 +554,7 @@ msgid "Manage active sessions, users and seats"
 msgstr "Управление текущими сеансами, пользователями и рабочими местами"
 
 #: src/login/org.freedesktop.login1.policy:322
-msgid ""
-"Authentication is required for managing active sessions, users and seats."
+msgid "Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Для управления текущими сеансами, пользователями и рабочими местами, "
 "необходимо пройти аутентификацию."
@@ -509,26 +570,68 @@ msgstr ""
 "аутентификацию."
 
 #: src/login/org.freedesktop.login1.policy:341
-msgid "Allow indication to the firmware to boot to setup interface"
-msgstr "РазÑ\80еÑ\88иÑ\82Ñ\8c Ð·Ð°Ð³Ñ\80Ñ\83зкÑ\83 Ð² Ñ\80ежиме Ð½Ð°Ñ\81Ñ\82Ñ\80ойки Ð¿Ñ\80оÑ\88ивки Ð¼Ð°Ñ\82еÑ\80инÑ\81кой Ð¿Ð»Ð°Ñ\82Ñ\8b"
+msgid "Set the reboot \"reason\" in the kernel"
+msgstr "УÑ\81Ñ\82ановиÑ\82Ñ\8c \"пÑ\80иÑ\87инÑ\83\" Ð¿ÐµÑ\80езагÑ\80Ñ\83зки"
 
 #: src/login/org.freedesktop.login1.policy:342
+msgid "Authentication is required to set the reboot \"reason\" in the kernel."
+msgstr ""
+"Чтобы установить \"причину\" перезагрузки, необходимо пройти аутентификацию."
+
+#: src/login/org.freedesktop.login1.policy:352
+msgid "Indicate to the firmware to boot to setup interface"
+msgstr ""
+"Запустить режим настройки прошивки материнской платы при следующей загрузке"
+
+#: src/login/org.freedesktop.login1.policy:353
 msgid ""
 "Authentication is required to indicate to the firmware to boot to setup "
 "interface."
 msgstr ""
-"Чтобы разрешить загрузку в режиме настройки прошивки материнской платы, "
+"Чтобы запустить режим настройки прошивки материнской платы, "
+"необходимо пройти аутентификацию."
+
+#: src/login/org.freedesktop.login1.policy:363
+msgid "Indicate to the boot loader to boot to the boot loader menu"
+msgstr "Отобразить меню загрузчика при следующей загрузке"
+
+#: src/login/org.freedesktop.login1.policy:364
+msgid ""
+"Authentication is required to indicate to the boot loader to boot to the "
+"boot loader menu."
+msgstr ""
+"Чтобы отобразить меню загрузчика при следующей загрузке, "
+"необходимо пройти аутентификацию."
+
+#: src/login/org.freedesktop.login1.policy:374
+msgid "Indicate to the boot loader to boot a specific entry"
+msgstr "Выбрать определённую загрузочную запись при следующем запуске"
+
+#: src/login/org.freedesktop.login1.policy:375
+msgid ""
+"Authentication is required to indicate to the boot loader to boot into a "
+"specific boot loader entry."
+msgstr ""
+"Чтобы установить определённую загрузочную запись для загрузки, "
 "необходимо пройти аутентификацию."
 
-#: src/login/org.freedesktop.login1.policy:351
+#: src/login/org.freedesktop.login1.policy:385
 msgid "Set a wall message"
 msgstr "Отправить сообщение на все терминалы"
 
-#: src/login/org.freedesktop.login1.policy:352
+#: src/login/org.freedesktop.login1.policy:386
 msgid "Authentication is required to set a wall message"
 msgstr ""
 "Чтобы отправить сообщение на все терминалы, необходимо пройти аутентификацию."
 
+#: src/login/org.freedesktop.login1.policy:395
+msgid "Change Session"
+msgstr "Сменить сессию"
+
+#: src/login/org.freedesktop.login1.policy:396
+msgid "Authentication is required to change the virtual terminal."
+msgstr "Чтобы сменить виртуальный терминал, необходимо пройти аутентификацию."
+
 #: src/machine/org.freedesktop.machine1.policy:22
 msgid "Log into a local container"
 msgstr "Зайти в локальный контейнер"
@@ -609,14 +712,151 @@ msgstr ""
 "Для управления образами виртуальных машин и контейнеров, необходимо пройти "
 "аутентификацию."
 
+#: src/network/org.freedesktop.network1.policy:22
+msgid "Set NTP servers"
+msgstr "Задать NTP-серверы"
+
+#: src/network/org.freedesktop.network1.policy:23
+msgid "Authentication is required to set NTP servers."
+msgstr "Чтобы задать NTP-серверы, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:33
+#: src/resolve/org.freedesktop.resolve1.policy:44
+msgid "Set DNS servers"
+msgstr "Задать DNS-серверы"
+
+#: src/network/org.freedesktop.network1.policy:34
+#: src/resolve/org.freedesktop.resolve1.policy:45
+msgid "Authentication is required to set DNS servers."
+msgstr "Чтобы задать DNS-серверы, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:44
+#: src/resolve/org.freedesktop.resolve1.policy:55
+msgid "Set domains"
+msgstr "Задать домены"
+
+#: src/network/org.freedesktop.network1.policy:45
+#: src/resolve/org.freedesktop.resolve1.policy:56
+msgid "Authentication is required to set domains."
+msgstr "Чтобы задать домены, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:55
+#: src/resolve/org.freedesktop.resolve1.policy:66
+msgid "Set default route"
+msgstr "Задать маршрут по умолчанию"
+
+#: src/network/org.freedesktop.network1.policy:56
+#: src/resolve/org.freedesktop.resolve1.policy:67
+msgid "Authentication is required to set default route."
+msgstr "Чтобы задать маршрут по умолчанию, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:66
+#: src/resolve/org.freedesktop.resolve1.policy:77
+msgid "Enable/disable LLMNR"
+msgstr "Включить/отключить LLMNR"
+
+#: src/network/org.freedesktop.network1.policy:67
+#: src/resolve/org.freedesktop.resolve1.policy:78
+msgid "Authentication is required to enable or disable LLMNR."
+msgstr "Чтобы включить или отключить LLMNR, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:77
+#: src/resolve/org.freedesktop.resolve1.policy:88
+msgid "Enable/disable multicast DNS"
+msgstr "Включить/отключить multicast DNS"
+
+#: src/network/org.freedesktop.network1.policy:78
+#: src/resolve/org.freedesktop.resolve1.policy:89
+msgid "Authentication is required to enable or disable multicast DNS."
+msgstr ""
+"Чтобы включить или отключить multicast DNS, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:88
+#: src/resolve/org.freedesktop.resolve1.policy:99
+msgid "Enable/disable DNS over TLS"
+msgstr "Включить/отключить DNS поверх TLS"
+
+#: src/network/org.freedesktop.network1.policy:89
+#: src/resolve/org.freedesktop.resolve1.policy:100
+msgid "Authentication is required to enable or disable DNS over TLS."
+msgstr ""
+"Чтобы включить или отключить DNS поверх TLS, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:99
+#: src/resolve/org.freedesktop.resolve1.policy:110
+msgid "Enable/disable DNSSEC"
+msgstr "Включить/отключить DNSSEC"
+
+#: src/network/org.freedesktop.network1.policy:100
+#: src/resolve/org.freedesktop.resolve1.policy:111
+msgid "Authentication is required to enable or disable DNSSEC."
+msgstr "Чтобы включить или отключить DNSSEC, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:110
+#: src/resolve/org.freedesktop.resolve1.policy:121
+msgid "Set DNSSEC Negative Trust Anchors"
+msgstr "Задать DNSSEC Negative Trust Anchors"
+
+#: src/network/org.freedesktop.network1.policy:111
+#: src/resolve/org.freedesktop.resolve1.policy:122
+msgid "Authentication is required to set DNSSEC Negative Trust Anchors."
+msgstr ""
+"Чтобы задать DNSSEC Negative Trust Anchors, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:121
+msgid "Revert NTP settings"
+msgstr "Восстановить настройки NTP по умолчанию"
+
+#: src/network/org.freedesktop.network1.policy:122
+msgid "Authentication is required to reset NTP settings."
+msgstr ""
+"Чтобы сбросить локальные настройки NTP, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:132
+msgid "Revert DNS settings"
+msgstr "Восстановить настройки DNS по умолчанию"
+
+#: src/network/org.freedesktop.network1.policy:133
+msgid "Authentication is required to reset DNS settings."
+msgstr ""
+"Чтобы сбросить локальные настройки DNS, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:143
+msgid "Renew dynamic addresses"
+msgstr "Обновить динамические адреса"
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to renew dynamic addresses."
+msgstr "Чтобы обновить динамические адреса, необходимо пройти аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:154
+msgid "Reload network settings"
+msgstr "Перечитать настройки сети"
+
+#: src/network/org.freedesktop.network1.policy:155
+msgid "Authentication is required to reload network settings."
+msgstr ""
+"Чтобы заставить systemd перечитать настройки сети, необходимо пройти "
+"аутентификацию."
+
+#: src/network/org.freedesktop.network1.policy:165
+msgid "Reconfigure network interface"
+msgstr "Изменить конфигурацию сетевого интерфейса"
+
+#: src/network/org.freedesktop.network1.policy:166
+msgid "Authentication is required to reconfigure network interface."
+msgstr ""
+"Чтобы изменить конфигурацию сетевого интерфейса, необходимо пройти"
+" аутентификацию."
+
 #: src/portable/org.freedesktop.portable1.policy:13
 msgid "Inspect a portable service image"
 msgstr "Прочитать образ переносимой службы"
 
 #: src/portable/org.freedesktop.portable1.policy:14
 msgid "Authentication is required to inspect a portable service image."
-msgstr "Чтобы прочитать образ переносимой службы, необходимо пройти "
-"аутентификацию."
+msgstr ""
+"ЧÑ\82обÑ\8b Ð¿Ñ\80оÑ\87иÑ\82аÑ\82Ñ\8c Ð¾Ð±Ñ\80аз Ð¿ÐµÑ\80еноÑ\81имой Ñ\81лÑ\83жбÑ\8b, Ð½ÐµÐ¾Ð±Ñ\85одимо Ð¿Ñ\80ойÑ\82и Ð°Ñ\83Ñ\82енÑ\82иÑ\84икаÑ\86иÑ\8e."
 
 #: src/portable/org.freedesktop.portable1.policy:23
 msgid "Attach or detach a portable service image"
@@ -625,8 +865,9 @@ msgstr "Подключить или отключить образ перенос
 #: src/portable/org.freedesktop.portable1.policy:24
 msgid ""
 "Authentication is required to attach or detach a portable service image."
-msgstr "Чтобы подключить или отключить образ переносимой службы, необходимо "
-"пройти аутентификацию."
+msgstr ""
+"Чтобы подключить или отключить образ переносимой службы, необходимо пройти "
+"аутентификацию."
 
 #: src/portable/org.freedesktop.portable1.policy:34
 msgid "Delete or modify portable service image"
@@ -645,8 +886,8 @@ msgstr "Зарегистрировать службу в DNS-SD"
 
 #: src/resolve/org.freedesktop.resolve1.policy:23
 msgid "Authentication is required to register a DNS-SD service"
-msgstr "Чтобы зарегистрировать службу в DNS-SD, необходимо пройти "
-"аутентификацию."
+msgstr ""
+"ЧÑ\82обÑ\8b Ð·Ð°Ñ\80егиÑ\81Ñ\82Ñ\80иÑ\80оваÑ\82Ñ\8c Ñ\81лÑ\83жбÑ\83 Ð² DNS-SD, Ð½ÐµÐ¾Ð±Ñ\85одимо Ð¿Ñ\80ойÑ\82и Ð°Ñ\83Ñ\82енÑ\82иÑ\84икаÑ\86иÑ\8e."
 
 #: src/resolve/org.freedesktop.resolve1.policy:33
 msgid "Unregister a DNS-SD service"
@@ -656,6 +897,15 @@ msgstr "Удалить службу из DNS-SD"
 msgid "Authentication is required to unregister a DNS-SD service"
 msgstr "Чтобы удалить службу из DNS-SD, необходимо пройти аутентификацию."
 
+#: src/resolve/org.freedesktop.resolve1.policy:132
+msgid "Revert name resolution settings"
+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"
 msgstr "Настроить системное время"
@@ -696,49 +946,47 @@ msgstr ""
 "Чтобы включить или выключить синхронизацию времени по сети, необходимо "
 "пройти аутентификацию."
 
-#: src/core/dbus-unit.c:326
+#: src/core/dbus-unit.c:356
 msgid "Authentication is required to start '$(unit)'."
 msgstr "Чтобы запустить «$(unit)», необходимо пройти аутентификацию."
 
-#: src/core/dbus-unit.c:327
+#: src/core/dbus-unit.c:357
 msgid "Authentication is required to stop '$(unit)'."
 msgstr "Чтобы остановить «$(unit)», необходимо пройти аутентификацию."
 
-#: src/core/dbus-unit.c:328
+#: src/core/dbus-unit.c:358
 msgid "Authentication is required to reload '$(unit)'."
 msgstr ""
 "Чтобы заставить «$(unit)» перечитать конфигурацию, необходимо пройти "
 "аутентификацию."
 
-#: src/core/dbus-unit.c:329 src/core/dbus-unit.c:330
+#: 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:437
-msgid "Authentication is required to kill '$(unit)'."
-msgstr "Чтобы убить юнит «$(unit)», необходимо пройти аутентификацию."
+#: 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:468
+#: src/core/dbus-unit.c:563
 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
 msgstr ""
 "Чтобы сбросить состояние «failed» у юнита «$(unit)», необходимо пройти "
 "аутентификацию."
 
-#: src/core/dbus-unit.c:501
+#: src/core/dbus-unit.c:596
 msgid "Authentication is required to set properties on '$(unit)'."
-msgstr "Чтобы изменить параметры юнита «$(unit)», необходимо пройти "
-"аутентификацию."
+msgstr ""
+"ЧÑ\82обÑ\8b Ð¸Ð·Ð¼ÐµÐ½Ð¸Ñ\82Ñ\8c Ð¿Ð°Ñ\80амеÑ\82Ñ\80Ñ\8b Ñ\8eниÑ\82а Â«$(unit)», Ð½ÐµÐ¾Ð±Ñ\85одимо Ð¿Ñ\80ойÑ\82и Ð°Ñ\83Ñ\82енÑ\82иÑ\84икаÑ\86иÑ\8e."
 
-#~ msgid "Press Ctrl+C to cancel all filesystem checks in progress"
-#~ msgstr ""
-#~ "Чтобы прервать все запущенные проверки файловых систем, нажмите Ctrl+C"
-
-# There is no difference between "на 2 дисках" (plural==1) and "на 5 дисках" (plural==2)
-#~ msgid "Checking in progress on %d disk (%3.1f%% complete)"
-#~ msgid_plural "Checking in progress on %d disks (%3.1f%% complete)"
-#~ msgstr[0] ""
-#~ "Проверяется целостность файловой системы на %d диске (выполнено %3.1f%%)"
-#~ msgstr[1] ""
-#~ "Проверяется целостность файловых систем на %d дисках (выполнено %3.1f%%)"
-#~ msgstr[2] ""
-#~ "Проверяется целостность файловых систем на %d дисках (выполнено %3.1f%%)"
+#: src/core/dbus-unit.c:705
+msgid ""
+"Authentication is required to delete files and directories associated with "
+"'$(unit)'."
+msgstr ""
+"Чтобы удалить файлы и директории, относящиеся к юниту «$(unit)», необходимо"
+" пройти аутентификацию."
index 41d8279e0a3fbb207f16784f1203976b125a6829..ced96e8045ab326e8359ebce7012bc49a1f25208 100644 (file)
--- a/po/sk.po
+++ b/po/sk.po
@@ -251,7 +251,7 @@ msgid "Allow attaching devices to seats"
 msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:24
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:25
@@ -260,7 +260,7 @@ msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:26
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:27
@@ -268,7 +268,7 @@ msgid "Power off the system"
 msgstr "Vypnutie systému"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:28
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Vyžaduje sa overenie totožnosti na vypnutie systému."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:29
@@ -277,20 +277,20 @@ msgstr "Vypnutie systému, pokiaľ sú prihlásení iní používatelia"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:30
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Vyžaduje sa overenie totožnosti na vypnutie systému, pokiaľ sú prihlásení "
 "iní používatelia."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:31
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:32
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:33
@@ -298,7 +298,7 @@ msgid "Reboot the system"
 msgstr "Reštart systému"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:34
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Vyžaduje sa overenie totožnosti na reštartovanie systému."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:35
@@ -307,20 +307,20 @@ msgstr "Reštart systému, pokiaľ sú prihlásení iní používatelia"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:36
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "Vyžaduje sa overenie totožnosti na reštartovanie systému, pokiaľ sú "
 "prihlásení iní používatelia."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:37
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:38
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:39
@@ -328,7 +328,7 @@ msgid "Suspend the system"
 msgstr "Uspanie systému"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:40
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Vyžaduje sa overenie totožnosti na uspanie systému."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:41
@@ -337,20 +337,20 @@ msgstr "Uspanie systému, pokiaľ sú prihlásení iní používatelia"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:42
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Vyžaduje sa overenie totožnosti na uspanie systému, pokiaľ sú prihlásení iní "
 "používatelia."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:43
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:44
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:45
@@ -358,7 +358,7 @@ msgid "Hibernate the system"
 msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:46
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:47
@@ -367,18 +367,18 @@ msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:48
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:49
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:50
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:51
@@ -387,7 +387,7 @@ msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:52
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr ""
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:53
index 422e9fa827e7e452774626a51b40b5b67ebb08c6..3d0bebd503619a3fff9d43156b0d9441f71510ef 100644 (file)
--- a/po/sr.po
+++ b/po/sr.po
@@ -278,7 +278,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Дозволи качење уређаја на седишта"
 
 #: src/login/org.freedesktop.login1.policy.in:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr "Потребно је да се идентификујете да бисте  закачили уређај на седиште."
 
 #: src/login/org.freedesktop.login1.policy.in:148
@@ -287,7 +287,7 @@ msgstr "Испери уређај да би уседиштио закачено"
 
 #: src/login/org.freedesktop.login1.policy.in:149
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Потребно је да се идентификујете да бисте поново подесили како се уређаји "
 "каче на седишта."
@@ -297,7 +297,7 @@ msgid "Power off the system"
 msgstr "Искључи систем"
 
 #: src/login/org.freedesktop.login1.policy.in:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Потребно је да се идентификујете да бисте искључили систем."
 
 #: src/login/org.freedesktop.login1.policy.in:169
@@ -306,20 +306,20 @@ msgstr "Искључи систем док су други корисници п
 
 #: src/login/org.freedesktop.login1.policy.in:170
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Потребно је да се идентификујете да бисте искључили систем док су други "
 "корисници пријављени."
 
 #: src/login/org.freedesktop.login1.policy.in:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "Искључи систем иако је програм затражио да се спречи гашење"
 
 #: src/login/org.freedesktop.login1.policy.in:181
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 "Потребно је да се идентификујете да бисте искључили систем иако је програм "
 "затражио да се спречи гашење система."
@@ -329,7 +329,7 @@ msgid "Reboot the system"
 msgstr "Поново покрени систем"
 
 #: src/login/org.freedesktop.login1.policy.in:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Потребно је да се идентификујете да бисте поново покренули систем."
 
 #: src/login/org.freedesktop.login1.policy.in:202
@@ -338,20 +338,20 @@ msgstr "Поново покрени систем док су други кори
 
 #: src/login/org.freedesktop.login1.policy.in:203
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "Потребно је да се идентификујете да бисте поново покренули систем док су "
 "други корисници пријављени."
 
 #: src/login/org.freedesktop.login1.policy.in:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr "Поново покрени систем иако је програм затражио да се спречи гашење"
 
 #: src/login/org.freedesktop.login1.policy.in:214
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 "Потребно је да се идентификујете да бисте поново покренули систем иако је "
 "програм затражио да се спречи гашење система."
@@ -361,7 +361,7 @@ msgid "Halt the system"
 msgstr "Заустави систем"
 
 #: src/login/org.freedesktop.login1.policy.in:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
 msgstr "Потребно је да се идентификујете да бисте зауставили систем."
 
 #: src/login/org.freedesktop.login1.policy.in:235
@@ -370,19 +370,19 @@ msgstr "Заустави систем док су други корисници
 
 #: src/login/org.freedesktop.login1.policy.in:236
 msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt the system while other users are "
 "logged in."
 msgstr ""
 "Потребно је да се идентификујете да бисте зауставили систем док су други "
 "корисници пријављени."
 
 #: src/login/org.freedesktop.login1.policy.in:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
 msgstr "Заустави систем иако програм тражи да се спречи заустављање"
 
 #: src/login/org.freedesktop.login1.policy.in:247
 msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
 "to inhibit it."
 msgstr ""
 "Потребно је да се идентификујете да бисте зауставили систем иако програм "
@@ -393,7 +393,7 @@ msgid "Suspend the system"
 msgstr "Обустави систем"
 
 #: src/login/org.freedesktop.login1.policy.in:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Потребно је да се идентификујете да бисте обуставили систем."
 
 #: src/login/org.freedesktop.login1.policy.in:267
@@ -402,20 +402,20 @@ msgstr "Обустави систем док су други корисници
 
 #: src/login/org.freedesktop.login1.policy.in:268
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Потребно је да се идентификујете да бисте обуставили систем док су други "
 "корисници пријављени."
 
 #: src/login/org.freedesktop.login1.policy.in:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr "Обуставите систем иако програм тражи да се спречи обустава"
 
 #: src/login/org.freedesktop.login1.policy.in:279
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 "Потребно је да се идентификујете да бисте обуставили систем иако је програм "
 "затражио да се спречи обустава система."
@@ -425,7 +425,7 @@ msgid "Hibernate the system"
 msgstr "Успавај систем"
 
 #: src/login/org.freedesktop.login1.policy.in:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "Потребно је да се идентификујете да бисте успавали систем."
 
 #: src/login/org.freedesktop.login1.policy.in:299
@@ -434,20 +434,20 @@ msgstr "Успавај систем док су други корисници п
 
 #: src/login/org.freedesktop.login1.policy.in:300
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Потребно је да се идентификујете да бисте успавали систем док су други "
 "корисници пријављени."
 
 #: src/login/org.freedesktop.login1.policy.in:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr "Успавај систем иако је програм затражио да се спречи спавање"
 
 #: src/login/org.freedesktop.login1.policy.in:311
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 "Потребно је да се идентификујете да бисте успавали систем иако је програм "
 "затражио да се спречи успављивање система."
@@ -458,7 +458,7 @@ msgstr "Управљај покренутим сесијама, корисниц
 
 #: src/login/org.freedesktop.login1.policy.in:322
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Потребно је да се идентификујете да бисте управљали покренутим сесијама, "
 "корисницима и седиштима."
index 6aeca0874b327573b0bab96b8a59b2573f25bc80..5f40d455ab3c20deb798018820b0942754025d26 100644 (file)
--- a/po/sv.po
+++ b/po/sv.po
@@ -259,7 +259,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Tillåt att binda enheter till platser"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:24
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr "Autentisering krävs för att binda en enhet till en plats."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:25
@@ -268,7 +268,7 @@ msgstr "Töm bindningar för enhet-till-plats"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:26
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Autentisering krävs för att återställa hur enheter är bundna till platser."
 
@@ -277,7 +277,7 @@ msgid "Power off the system"
 msgstr "Stäng av systemet"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:28
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Autentisering krävs för att stänga av systemet."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:29
@@ -286,20 +286,20 @@ msgstr "Stäng av systemet medan andra användare är inloggade"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:30
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Autentisering krävs för att stänga av systemet medan andra användare är "
 "inloggade."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:31
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "Stäng av systemet även då ett program hindrar det"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:32
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 "Autentisering krävs för att stänga av systemet även då ett program hindrar "
 "det."
@@ -309,7 +309,7 @@ msgid "Reboot the system"
 msgstr "Starta om systemet"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:34
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Autentisering krävs för att starta om systemet."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:35
@@ -318,20 +318,20 @@ msgstr "Starta om systemet medan andra användare är inloggade"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:36
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "Autentisering krävs för att starta om systemet medan andra användare är "
 "inloggade."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:37
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr "Starta om systemet även då ett program hindrar det"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:38
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 "Autentisering krävs för att starta om systemet även då ett program hindrar "
 "det."
@@ -341,7 +341,7 @@ msgid "Halt the system"
 msgstr "Stoppa systemet"
 
 #: src/login/org.freedesktop.login1.policy.in:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
 msgstr "Autentisering krävs för att stoppa systemet."
 
 #: src/login/org.freedesktop.login1.policy.in:235
@@ -350,19 +350,19 @@ msgstr "Stoppa systemet medan andra användare är inloggade"
 
 #: src/login/org.freedesktop.login1.policy.in:236
 msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt the system while other users are "
 "logged in."
 msgstr ""
 "Autentisering krävs för att stoppa systemet medan andra användare är "
 "inloggade."
 
 #: src/login/org.freedesktop.login1.policy.in:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
 msgstr "Stoppa systemet även då ett program hindrar det"
 
 #: src/login/org.freedesktop.login1.policy.in:247
 msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
 "to inhibit it."
 msgstr ""
 "Autentisering krävs för att stoppa systemet även då ett program hindrar det."
@@ -372,7 +372,7 @@ msgid "Suspend the system"
 msgstr "Försätt system i vänteläge"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:40
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Autentisering krävs för att försätta system i vänteläge."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:41
@@ -381,20 +381,20 @@ msgstr "Försätt systemet i vänteläge medan andra användare är inloggade"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:42
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Autentisering krävs för att försätta systemet i vänteläge medan andra "
 "användare är inloggade."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:43
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr "Försätt systemet i vänteläge även då ett program hindrar det"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:44
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 "Autentisering krävs för att försätta ett program i vänteläge även då ett "
 "program hindrar det."
@@ -404,7 +404,7 @@ msgid "Hibernate the system"
 msgstr "Försätt systemet i viloläge"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:46
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "Autentisering krävs för att försätta systemet i viloläge."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:47
@@ -413,20 +413,20 @@ msgstr "Försätt systemet i viloläge medan andra användare är inloggade"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:48
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Autentisering krävs för att försätta systemet i viloläge medan andra "
 "användare är inloggade."
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:49
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr "Försätt systemet i viloläge även då ett program hindrar det"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:50
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 "Autentisering krävs för att försätta ett program i viloläge även då ett "
 "program hindrar det."
@@ -437,7 +437,7 @@ msgstr "Hantera aktiva sessioner, användare och platser"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:52
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Autentisering krävs för att hantera aktiva sessioner, användare och platser."
 
index e0239594afd1c9556c99af4c47393451429d442a..86c13448e23290009568f82d59a8ffdd2b867f40 100644 (file)
--- a/po/tr.po
+++ b/po/tr.po
@@ -281,7 +281,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Aygıtların yuvaya takılmasına izin ver"
 
 #: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr ""
 "Bir aygıtın yuvaya takılmasına izin vermek kimlik doğrulaması gerektiriyor."
 
@@ -291,7 +291,7 @@ msgstr "Aygıtın yuvaya eklenmesini sıfırla"
 
 #: src/login/org.freedesktop.login1.policy:149
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Aygıtların yuvalara nasıl takıldığını sıfırlamak kimlik doğrulama "
 "gerektiriyor."
@@ -301,7 +301,7 @@ msgid "Power off the system"
 msgstr "Sistemi kapat"
 
 #: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Sistemi kapatmak için kimlik doğrulaması gerekiyor."
 
 #: src/login/org.freedesktop.login1.policy:169
@@ -310,20 +310,20 @@ msgstr "Diğer kullanıcılar oturum açmışken sistemi kapat"
 
 #: src/login/org.freedesktop.login1.policy:170
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Diğer kullanıcılar oturum açmışken sistemi kapatmak kimlik doğrulaması "
 "gerektiriyor."
 
 #: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "Bir uygulama engellenmesini isterken sistemi kapat"
 
 #: src/login/org.freedesktop.login1.policy:181
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 "Bir uygulama engellenmesini isterken sistemi kapatmak kimlik doğrulaması "
 "gerektiriyor."
@@ -333,7 +333,7 @@ msgid "Reboot the system"
 msgstr "Sistemi yeniden başlat"
 
 #: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Sistemi yeniden başlatmak kimlik doğrulaması gerektiriyor."
 
 #: src/login/org.freedesktop.login1.policy:202
@@ -342,20 +342,20 @@ msgstr "Diğer kullanıcılar oturum açmışken sistemi yeniden başlat"
 
 #: src/login/org.freedesktop.login1.policy:203
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "Diğer kullanıcılar oturum açmışken sistemi yeniden başlatmak kimlik "
 "doğrulaması gerektiriyor."
 
 #: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr "Bir uygulama engellenmesini isterken sistemi yeniden başlat"
 
 #: src/login/org.freedesktop.login1.policy:214
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 "Bir uygulama engellenmesini isterken sistemi yeniden başlatmak kimlik "
 "doğrulaması gerektiriyor."
@@ -365,7 +365,7 @@ msgid "Halt the system"
 msgstr "Sistemi durdur"
 
 #: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
 msgstr "Sistemi durdurmak kimlik doğrulaması gerektiriyor."
 
 #: src/login/org.freedesktop.login1.policy:235
@@ -374,19 +374,19 @@ msgstr "Diğer kullanıcılar oturum açmışken sistemi durdur"
 
 #: src/login/org.freedesktop.login1.policy:236
 msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt the system while other users are "
 "logged in."
 msgstr ""
 "Diğer kullanıcılar oturum açmışken sistemi durdurmak kimlik doğrulaması "
 "gerektiriyor."
 
 #: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
 msgstr "Bir uygulama engellenmesini isterken sistemi durdur"
 
 #: src/login/org.freedesktop.login1.policy:247
 msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
 "to inhibit it."
 msgstr ""
 "Bir uygulama engellenmesini isterken sistemi durdurmak kimlik doğrulaması "
@@ -397,7 +397,7 @@ msgid "Suspend the system"
 msgstr "Sistemi askıya al"
 
 #: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Sistemi askıya almak kimlik doğrulaması gerektiriyor."
 
 #: src/login/org.freedesktop.login1.policy:267
@@ -406,20 +406,20 @@ msgstr "Diğer kullanıcılar oturum açmışken sistemi askıya al"
 
 #: src/login/org.freedesktop.login1.policy:268
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Diğer kullanıcılar oturum açmışken sistemi askıya almak kimlik doğrulaması "
 "gerektiriyor."
 
 #: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr "Bir uygulama engellenmesini isterken sistemi askıya al"
 
 #: src/login/org.freedesktop.login1.policy:279
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 "Bir uygulama engellenmesini isterken sistemi askıya almak kimlik doğrulaması "
 "gerektiriyor."
@@ -429,7 +429,7 @@ msgid "Hibernate the system"
 msgstr "Sistemi hazırda beklet"
 
 #: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "Sistemi hazırda bekletmek kimlik doğrulaması gerektiriyor."
 
 #: src/login/org.freedesktop.login1.policy:299
@@ -438,20 +438,20 @@ msgstr "Diğer kullanıcılar oturum açmışken sistemi hazırda beklet"
 
 #: src/login/org.freedesktop.login1.policy:300
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Diğer kullanıcılar oturum açmışken sistemi hazırda bekletmek kimlik "
 "doğrulaması gerektiriyor."
 
 #: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr "Bir uygulama engellenmesini isterken sistemi hazırda beklet"
 
 #: src/login/org.freedesktop.login1.policy:311
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 "Bir uygulama engellenmesini isterken sistemi hazırda bekletmek kimlik "
 "doğrulaması gerektiriyor."
@@ -462,7 +462,7 @@ msgstr "Aktif oturumları, kullanıcıları ve yuvaları yönet"
 
 #: src/login/org.freedesktop.login1.policy:322
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Aktif oturumları, kullanıcıları ve yuvaları yönetmek için kimlik doğrulaması "
 "gereklidir."
index 96c0c55053108e108a47b5d96a70be06f5445791..c2902797dbbbb8aae054e9c77b3424022946e699 100644 (file)
--- a/po/uk.po
+++ b/po/uk.po
@@ -2,13 +2,13 @@
 # Ukrainian translation for systemd.
 # Eugene Melnik <jeka7js@gmail.com>, 2014.
 # Daniel Korostil <ted.korostiled@gmail.com>, 2014, 2016, 2018.
-# Yuri Chornoivan <yurchor@ukr.net>, 2019.
+# Yuri Chornoivan <yurchor@ukr.net>, 2019, 2020.
 msgid ""
 msgstr ""
 "Project-Id-Version: systemd master\n"
 "Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
-"POT-Creation-Date: 2019-07-29 15:34+0000\n"
-"PO-Revision-Date: 2019-08-16 09:11+0300\n"
+"POT-Creation-Date: 2020-01-30 15:31+0000\n"
+"PO-Revision-Date: 2020-02-07 12:37+0200\n"
 "Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
 "Language-Team: Ukrainian <kde-i18n-uk@kde.org>\n"
 "Language: uk\n"
@@ -17,7 +17,7 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<"
 "=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-"X-Generator: Lokalize 19.11.70\n"
+"X-Generator: Lokalize 20.03.70\n"
 
 #: src/core/org.freedesktop.systemd1.policy.in:22
 msgid "Send passphrase back to system"
@@ -70,6 +70,60 @@ msgstr "Перезапустити стан системи"
 msgid "Authentication is required to reload the systemd state."
 msgstr "Потрібна автентифікація, щоб перезапустити стан системи."
 
+#: src/home/org.freedesktop.home1.policy:13
+msgid "Create a home area"
+msgstr "Створення домашньої теки"
+
+#: src/home/org.freedesktop.home1.policy:14
+msgid "Authentication is required to create a user's home area."
+msgstr "Для створення домашньої теки користувача слід пройти розпізнавання."
+
+#: src/home/org.freedesktop.home1.policy:23
+msgid "Remove a home area"
+msgstr "Вилучення домашньої теки"
+
+#: src/home/org.freedesktop.home1.policy:24
+msgid "Authentication is required to remove a user's home area."
+msgstr "Для вилучення домашньої теки користувача слід пройти розпізнавання."
+
+#: src/home/org.freedesktop.home1.policy:33
+msgid "Check credentials of a home area"
+msgstr "Перевірка реєстраційних даних для доступу до домашньої теки"
+
+#: src/home/org.freedesktop.home1.policy:34
+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"
+msgstr "Оновлення домашньої теки"
+
+#: src/home/org.freedesktop.home1.policy:44
+msgid "Authentication is required to update a user's home area."
+msgstr "Для оновлення домашньої теки користувача слід пройти розпізнавання."
+
+#: src/home/org.freedesktop.home1.policy:53
+msgid "Resize a home area"
+msgstr "Зміна розмірів домашньої теки"
+
+#: src/home/org.freedesktop.home1.policy:54
+msgid "Authentication is required to resize a user's home area."
+msgstr ""
+"Для зміни розмірів домашньої теки користувача слід пройти розпізнавання."
+
+#: src/home/org.freedesktop.home1.policy:63
+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."
+msgstr ""
+"Для зміни пароля для доступу до домашньої теки користувача слід пройти"
+" розпізнавання."
+
 #: src/hostname/org.freedesktop.hostname1.policy:20
 msgid "Set host name"
 msgstr "Встановити назву вузла"
@@ -283,7 +337,7 @@ msgid "Allow attaching devices to seats"
 msgstr "Дозволити під'єднання пристроїв до місць"
 
 #: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr "Потрібна автентифікація, щоб під'єднувати пристрої до місць."
 
 #: src/login/org.freedesktop.login1.policy:148
@@ -292,7 +346,7 @@ msgstr "Очисний пристрій для під'єднань до місц
 
 #: src/login/org.freedesktop.login1.policy:149
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Потрібна автентифікація, щоб перезапустити спосіб під'єднання до місць."
 
@@ -301,7 +355,7 @@ msgid "Power off the system"
 msgstr "Вимкнути систему"
 
 #: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "Потрібна автентифікація, щоб вимкнути систему."
 
 #: src/login/org.freedesktop.login1.policy:169
@@ -310,19 +364,19 @@ msgstr "Вимкнути систему, коли інші користувач
 
 #: src/login/org.freedesktop.login1.policy:170
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr ""
 "Потрібна автентифікація, щоб вимкнути систему, коли інші користувачі в ній."
 
 #: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "Вимкнути систему, коли програми намагаються перешкодити цьому"
 
 #: src/login/org.freedesktop.login1.policy:181
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr ""
 "Потрібна автентифікація, щоб вимкнути систему, коли програми намагаються "
 "перешкодити цьому."
@@ -332,7 +386,7 @@ msgid "Reboot the system"
 msgstr "Перезавантажити систему"
 
 #: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "Для перезавантаження системи необхідна ідентифікація."
 
 #: src/login/org.freedesktop.login1.policy:202
@@ -341,20 +395,20 @@ msgstr "Перезавантажити, якщо інші користувачі
 
 #: src/login/org.freedesktop.login1.policy:203
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr ""
 "Потрібна автентифікація, щоб перезапустити систему, коли інші користувачі в "
 "ній."
 
 #: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr "Перезапустити систему, коли програми намагаються перешкодити цьому"
 
 #: src/login/org.freedesktop.login1.policy:214
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr ""
 "Потрібна автентифікація, щоб перезапустити систему, коли програми "
 "намагаються перешкодити цьому."
@@ -364,7 +418,7 @@ msgid "Halt the system"
 msgstr "Зупинити систему"
 
 #: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
 msgstr "Потрібна автентифікація, щоб зупинити систему."
 
 #: src/login/org.freedesktop.login1.policy:235
@@ -373,18 +427,18 @@ msgstr "Зупинити систему, коли інші користувач
 
 #: src/login/org.freedesktop.login1.policy:236
 msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt the system while other users are "
 "logged in."
 msgstr ""
 "Потрібна автентифікація, щоб зупинити систему, коли інші користувачі в ній."
 
 #: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
 msgstr "Зупинити систему, коли програми намагаються перешкодити цьому"
 
 #: src/login/org.freedesktop.login1.policy:247
 msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
 "to inhibit it."
 msgstr ""
 "Потрібна автентифікація, щоб зупинити систему, коли програми намагаються "
@@ -395,7 +449,7 @@ msgid "Suspend the system"
 msgstr "Призупинити систему"
 
 #: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "Потрібна автентифікація, щоб призупинити систему."
 
 #: src/login/org.freedesktop.login1.policy:267
@@ -404,20 +458,20 @@ msgstr "Призупинити систему, коли інші користу
 
 #: src/login/org.freedesktop.login1.policy:268
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr ""
 "Потрібна автентифікація, щоб призупинити систему, коли інші користувачі в "
 "ній."
 
 #: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr "Призупинити систему, коли програми намагаються перешкодити цьому"
 
 #: src/login/org.freedesktop.login1.policy:279
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr ""
 "Потрібна автентифікація, щоб призупинити систему, коли програми намагаються "
 "перешкодити цьому."
@@ -427,7 +481,7 @@ msgid "Hibernate the system"
 msgstr "Приспати систему"
 
 #: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "Потрібна автентифікація, щоб приспати систему."
 
 #: src/login/org.freedesktop.login1.policy:299
@@ -436,19 +490,19 @@ msgstr "Приспати систему, коли інші користувач
 
 #: src/login/org.freedesktop.login1.policy:300
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr ""
 "Потрібна автентифікація, щоб присипання систему, коли інші користувачі в ній."
 
 #: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr "Приспати систему, коли програми намагаються перешкодити цьому"
 
 #: src/login/org.freedesktop.login1.policy:311
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr ""
 "Потрібна автентифікація, щоб приспати систему, коли програми намагаються "
 "перешкодити цьому."
@@ -459,7 +513,7 @@ msgstr "Керувати сеансами, користувачами і роб
 
 #: src/login/org.freedesktop.login1.policy:322
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Потрібна автентифікація, щоб керувати сеансами, користувачами і робочими "
 "місцями."
@@ -525,6 +579,14 @@ msgstr "Вказати повідомлення на стіні"
 msgid "Authentication is required to set a wall message"
 msgstr "Потрібна автентифікація, щоб вказати повідомлення на стіні"
 
+#: src/login/org.freedesktop.login1.policy:395
+msgid "Change Session"
+msgstr "Зміна сеансу"
+
+#: src/login/org.freedesktop.login1.policy:396
+msgid "Authentication is required to change the virtual terminal."
+msgstr "Для зміни віртуального термінала слід пройти розпізнавання."
+
 #: src/machine/org.freedesktop.machine1.policy:22
 msgid "Log into a local container"
 msgstr "Увійти в локальний контейнер"
@@ -608,66 +670,82 @@ msgid "Authentication is required to set NTP servers."
 msgstr "Потрібна автентифікація, щоб встановити сервери NTP."
 
 #: src/network/org.freedesktop.network1.policy:33
+#: src/resolve/org.freedesktop.resolve1.policy:44
 msgid "Set DNS servers"
 msgstr "Встановлення серверів DNS"
 
 #: src/network/org.freedesktop.network1.policy:34
+#: src/resolve/org.freedesktop.resolve1.policy:45
 msgid "Authentication is required to set DNS servers."
 msgstr "Потрібна автентифікація, щоб встановити сервери DNS."
 
 #: src/network/org.freedesktop.network1.policy:44
+#: src/resolve/org.freedesktop.resolve1.policy:55
 msgid "Set domains"
 msgstr "Встановлення доменів"
 
 #: src/network/org.freedesktop.network1.policy:45
+#: src/resolve/org.freedesktop.resolve1.policy:56
 msgid "Authentication is required to set domains."
 msgstr "Потрібна автентифікація, щоб встановити домени."
 
 #: src/network/org.freedesktop.network1.policy:55
+#: src/resolve/org.freedesktop.resolve1.policy:66
 msgid "Set default route"
 msgstr "Встановлення типового маршруту"
 
 #: src/network/org.freedesktop.network1.policy:56
+#: src/resolve/org.freedesktop.resolve1.policy:67
 msgid "Authentication is required to set default route."
 msgstr "Потрібна автентифікація, щоб встановити типовий маршрут."
 
 #: src/network/org.freedesktop.network1.policy:66
+#: src/resolve/org.freedesktop.resolve1.policy:77
 msgid "Enable/disable LLMNR"
 msgstr "Вмикання або вимикання LLMNR"
 
 #: src/network/org.freedesktop.network1.policy:67
+#: src/resolve/org.freedesktop.resolve1.policy:78
 msgid "Authentication is required to enable or disable LLMNR."
 msgstr "Потрібна автентифікація, щоб увімкнути або вимкнути LLMNR."
 
 #: src/network/org.freedesktop.network1.policy:77
+#: src/resolve/org.freedesktop.resolve1.policy:88
 msgid "Enable/disable multicast DNS"
 msgstr "Вмикання або вимикання трансляційного DNS"
 
 #: src/network/org.freedesktop.network1.policy:78
+#: src/resolve/org.freedesktop.resolve1.policy:89
 msgid "Authentication is required to enable or disable multicast DNS."
 msgstr "Потрібна автентифікація, щоб увімкнути або вимкнути трансляційну DNS."
 
 #: src/network/org.freedesktop.network1.policy:88
+#: src/resolve/org.freedesktop.resolve1.policy:99
 msgid "Enable/disable DNS over TLS"
 msgstr "Вмикання і вимикання DNS через TLS"
 
 #: src/network/org.freedesktop.network1.policy:89
+#: src/resolve/org.freedesktop.resolve1.policy:100
 msgid "Authentication is required to enable or disable DNS over TLS."
 msgstr "Потрібна автентифікація, щоб увімкнути або вимкнути DNS через TLS."
 
 #: src/network/org.freedesktop.network1.policy:99
+#: src/resolve/org.freedesktop.resolve1.policy:110
 msgid "Enable/disable DNSSEC"
 msgstr "Вмикання або вимикання DNSSEC"
 
 #: src/network/org.freedesktop.network1.policy:100
+#: src/resolve/org.freedesktop.resolve1.policy:111
 msgid "Authentication is required to enable or disable DNSSEC."
 msgstr "Потрібна автентифікація, щоб увімкнути або вимкнути DNSSEC."
 
 #: src/network/org.freedesktop.network1.policy:110
+#: src/resolve/org.freedesktop.resolve1.policy:121
 msgid "Set DNSSEC Negative Trust Anchors"
 msgstr "Встановлення прив'язок від'ємної довіри DNSSEC"
 
 #: src/network/org.freedesktop.network1.policy:111
+#: src/resolve/org.freedesktop.resolve1.policy:122
 msgid "Authentication is required to set DNSSEC Negative Trust Anchors."
 msgstr ""
 "Потрібна автентифікація, щоб встановити прив'язки від'ємної довіри DNSSEC."
@@ -677,16 +755,40 @@ msgid "Revert NTP settings"
 msgstr "Повернення до початкових параметрів NTP"
 
 #: src/network/org.freedesktop.network1.policy:122
-msgid "Authentication is required to revert NTP settings."
-msgstr "Ð\9fоÑ\82Ñ\80Ñ\96бна Ð°Ð²Ñ\82енÑ\82иÑ\84Ñ\96каÑ\86Ñ\96Ñ\8f, Ñ\89об Ð¿Ð¾Ð²ÐµÑ\80нÑ\83Ñ\82иÑ\81Ñ\8f Ð´Ð¾ Ð¿Ð¾Ñ\87аÑ\82ковиÑ\85 Ð¿Ð°Ñ\80амеÑ\82Ñ\80Ñ\96в NTP."
+msgid "Authentication is required to reset NTP settings."
+msgstr "Ð\94лÑ\8f Ñ\81киданнÑ\8f Ð¿Ð°Ñ\80амеÑ\82Ñ\80Ñ\96в NTP Ð´Ð¾ Ñ\82иповиÑ\85 Ñ\81лÑ\96д Ð¿Ñ\80ойÑ\82и Ñ\80озпÑ\96знаваннÑ\8f."
 
 #: src/network/org.freedesktop.network1.policy:132
 msgid "Revert DNS settings"
 msgstr "Повернення до початкових параметрів DNS"
 
 #: src/network/org.freedesktop.network1.policy:133
-msgid "Authentication is required to revert DNS settings."
-msgstr "Потрібна автентифікація, щоб повернутися до початкових параметрів DNS."
+msgid "Authentication is required to reset DNS settings."
+msgstr "Для скидання параметрів DNS до типових слід пройти розпізнавання."
+
+#: src/network/org.freedesktop.network1.policy:143
+msgid "Renew dynamic addresses"
+msgstr "Оновлення динамічних адрес"
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to renew dynamic addresses."
+msgstr "Для оновлення динамічних адрес слід пройти розпізнавання."
+
+#: src/network/org.freedesktop.network1.policy:154
+msgid "Reload network settings"
+msgstr "Перезавантаження параметрів мережі"
+
+#: src/network/org.freedesktop.network1.policy:155
+msgid "Authentication is required to reload network settings."
+msgstr "Для перезавантаження параметрів мережі слід пройти розпізнавання."
+
+#: src/network/org.freedesktop.network1.policy:165
+msgid "Reconfigure network interface"
+msgstr "Переналаштування інтерфейсу мережі"
+
+#: src/network/org.freedesktop.network1.policy:166
+msgid "Authentication is required to reconfigure network interface."
+msgstr "Для зміни налаштувань інтерфейсу мережі слід пройти розпізнавання."
 
 #: src/portable/org.freedesktop.portable1.policy:13
 msgid "Inspect a portable service image"
@@ -733,6 +835,16 @@ msgstr "Зняти з реєстрації службу DNS-SD"
 msgid "Authentication is required to unregister a DNS-SD service"
 msgstr "Потрібна автентифікація, щоб зняти з реєстрації службу DNS-SD"
 
+#: src/resolve/org.freedesktop.resolve1.policy:132
+msgid "Revert name resolution settings"
+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"
 msgstr "Вказати системний час"
@@ -773,41 +885,41 @@ msgstr ""
 "Потрібна автентифікація, щоб контролювати, чи синхронізування часу через "
 "мережу запущено."
 
-#: src/core/dbus-unit.c:354
+#: src/core/dbus-unit.c:355
 msgid "Authentication is required to start '$(unit)'."
 msgstr "Потрібна автентифікація, щоб запустити «$(unit)»."
 
-#: src/core/dbus-unit.c:355
+#: src/core/dbus-unit.c:356
 msgid "Authentication is required to stop '$(unit)'."
 msgstr "Потрібна автентифікація, щоб зупинити «$(unit)»."
 
-#: src/core/dbus-unit.c:356
+#: src/core/dbus-unit.c:357
 msgid "Authentication is required to reload '$(unit)'."
 msgstr "Потрібна автентифікація, щоб перезавантажити «$(unit)»."
 
-#: src/core/dbus-unit.c:357 src/core/dbus-unit.c:358
+#: src/core/dbus-unit.c:358 src/core/dbus-unit.c:359
 msgid "Authentication is required to restart '$(unit)'."
 msgstr "Потрібна автентифікація, щоб перезапустити «$(unit)»."
 
-#: src/core/dbus-unit.c:530
+#: src/core/dbus-unit.c:531
 msgid ""
 "Authentication is required to send a UNIX signal to the processes of "
 "'$(unit)'."
 msgstr ""
 "Потрібна автентифікація, щоб надіслати сигнал UNIX до процесів «$(unit)»."
 
-#: src/core/dbus-unit.c:561
+#: src/core/dbus-unit.c:562
 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
 msgstr "Потрібна автентифікація, щоб скинути «пошкоджений» стан з «$(unit)»."
 
-#: src/core/dbus-unit.c:594
+#: src/core/dbus-unit.c:595
 msgid "Authentication is required to set properties on '$(unit)'."
 msgstr "Потрібна автентифікація, щоб вказати властивості на «$(unit)»."
 
-#: src/core/dbus-unit.c:703
+#: src/core/dbus-unit.c:704
 msgid ""
 "Authentication is required to delete files and directories associated with "
 "'$(unit)'."
 msgstr ""
-"Потрібна автентифікація, щоб вилучити файли і каталоги, які пов'язано із"
-" «$(unit)»."
+"Потрібна автентифікація, щоб вилучити файли і каталоги, які пов'язано із "
+"«$(unit)»."
index 18c966abbdfe3d6fa4864951a27557c8cc0c17db..7196cc50bff2f95e52656bdb99c88c1c30d86569 100644 (file)
@@ -232,7 +232,7 @@ msgstr "允许将设备附加至会话座位"
 #
 # To fully understand the meaning, please refer to session management in old ConsoleKit and new systemd-logind.
 #: ../src/login/org.freedesktop.login1.policy.in.h:22
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr "允许将设备附加至某个会话座位需要认证。"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:23
@@ -241,7 +241,7 @@ msgstr "刷新设备至会话座位间的连接"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:24
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr "重新设定设备的会话座位接入方式时需要认证。"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:25
@@ -249,7 +249,7 @@ msgid "Power off the system"
 msgstr "关闭系统"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:26
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "关闭系统需要认证。"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:27
@@ -258,18 +258,18 @@ msgstr "存在其他已登录用户时仍然关机"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:28
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr "存在其他已登录用户时关闭系统需要认证。"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:29
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "有其它应用程序阻止时仍然关机"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:30
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr "在其它应用程序阻止关机时关闭系统需要认证。"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:31
@@ -277,7 +277,7 @@ msgid "Reboot the system"
 msgstr "重启系统"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:32
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "重启系统需要认证。"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:33
@@ -286,18 +286,18 @@ msgstr "存在其他已登录用户时仍然重启"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:34
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr "存在其他已登录用户时重启系统需要认证。"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:35
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr "有其它应用程序阻止时仍然重启"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:36
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr "在其它应用程序阻止重启时重启系统需要认证。"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:37
@@ -305,7 +305,7 @@ msgid "Suspend the system"
 msgstr "挂起系统"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:38
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "挂起系统需要认证。"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:39
@@ -314,18 +314,18 @@ msgstr "存在其他已登录用户时仍然挂起系统"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:40
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr "存在其他已登录用户时挂起系统需要认证。"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:41
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr "有其它应用程序阻止时仍然挂起系统"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:42
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr "在其它应用程序阻止挂起时挂起系统需要认证。"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:43
@@ -333,7 +333,7 @@ msgid "Hibernate the system"
 msgstr "休眠系统"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:44
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "休眠系统需要认证。"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:45
@@ -342,18 +342,18 @@ msgstr "存在其他已登录用户时仍然休眠"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:46
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr "存在其他已登录用户时进行休眠系统需要认证。"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:47
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr "有其它应用程序阻止时仍然休眠"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:48
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr "在其它应用程序阻止休眠时进行休眠需要认证。"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:49
@@ -362,7 +362,7 @@ msgstr "管理活动会话、用户与会话座位"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:50
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr "管理活动会话、用户与会话座位需要认证。"
 
 #: ../src/login/org.freedesktop.login1.policy.in.h:51
index 4e5e4bf8b093b1d6246097cf1f5cd14954b19dcc..d14a338e5c4989609decea125f30676d2946905c 100644 (file)
@@ -239,7 +239,7 @@ msgid "Allow attaching devices to seats"
 msgstr "允許將設備連接到座位"
 
 #: src/login/org.freedesktop.login1.policy:138
-msgid "Authentication is required for attaching a device to a seat."
+msgid "Authentication is required to attach a device to a seat."
 msgstr "將設備連接到座位需要驗證。"
 
 #: src/login/org.freedesktop.login1.policy:148
@@ -248,7 +248,7 @@ msgstr "暴露裝置以安裝附件"
 
 #: src/login/org.freedesktop.login1.policy:149
 msgid ""
-"Authentication is required for resetting how devices are attached to seats."
+"Authentication is required to reset how devices are attached to seats."
 msgstr "要重置裝置如何連接到座位需要驗證。"
 
 #: src/login/org.freedesktop.login1.policy:158
@@ -256,7 +256,7 @@ msgid "Power off the system"
 msgstr "關閉系統電源"
 
 #: src/login/org.freedesktop.login1.policy:159
-msgid "Authentication is required for powering off the system."
+msgid "Authentication is required to power off the system."
 msgstr "關閉系統電源需要身份驗證。"
 
 #: src/login/org.freedesktop.login1.policy:169
@@ -265,18 +265,18 @@ msgstr "在有其他使用者登入時關閉系統電源"
 
 #: src/login/org.freedesktop.login1.policy:170
 msgid ""
-"Authentication is required for powering off the system while other users are "
+"Authentication is required to power off the system while other users are "
 "logged in."
 msgstr "在有其他使用者登入時關閉系統電源需要驗證。"
 
 #: src/login/org.freedesktop.login1.policy:180
-msgid "Power off the system while an application asked to inhibit it"
+msgid "Power off the system while an application is inhibiting this"
 msgstr "當應用程式阻止系統電源關閉時將其關閉"
 
 #: src/login/org.freedesktop.login1.policy:181
 msgid ""
-"Authentication is required for powering off the system while an application "
-"asked to inhibit it."
+"Authentication is required to power off the system while an application "
+"is inhibiting this."
 msgstr "當應用程式阻止系統電源關閉時將系統電源關閉需要驗證。"
 
 #: src/login/org.freedesktop.login1.policy:191
@@ -284,7 +284,7 @@ msgid "Reboot the system"
 msgstr "重新啟動系統"
 
 #: src/login/org.freedesktop.login1.policy:192
-msgid "Authentication is required for rebooting the system."
+msgid "Authentication is required to reboot the system."
 msgstr "重新啟動系統需要驗證。"
 
 #: src/login/org.freedesktop.login1.policy:202
@@ -293,18 +293,18 @@ msgstr "在有其他使用者登入時重新啟動系統"
 
 #: src/login/org.freedesktop.login1.policy:203
 msgid ""
-"Authentication is required for rebooting the system while other users are "
+"Authentication is required to reboot the system while other users are "
 "logged in."
 msgstr "在有其他使用者登入時重新啟動系統需要驗證。"
 
 #: src/login/org.freedesktop.login1.policy:213
-msgid "Reboot the system while an application asked to inhibit it"
+msgid "Reboot the system while an application is inhibiting this"
 msgstr "當應用程式阻止重新啟動系統時將系統重新啟動"
 
 #: src/login/org.freedesktop.login1.policy:214
 msgid ""
-"Authentication is required for rebooting the system while an application "
-"asked to inhibit it."
+"Authentication is required to reboot the system while an application "
+"is inhibiting this."
 msgstr "當應用程式阻止系統重新啟動時將系統重新啟動需要驗證。"
 
 #: src/login/org.freedesktop.login1.policy:224
@@ -312,7 +312,7 @@ msgid "Halt the system"
 msgstr "停止系統"
 
 #: src/login/org.freedesktop.login1.policy:225
-msgid "Authentication is required for halting the system."
+msgid "Authentication is required to halt the system."
 msgstr "停止系統需要身份驗證。"
 
 #: src/login/org.freedesktop.login1.policy:235
@@ -321,17 +321,17 @@ msgstr "在其他使用者登入時停止系統"
 
 #: src/login/org.freedesktop.login1.policy:236
 msgid ""
-"Authentication is required for halting the system while other users are "
+"Authentication is required to halt the system while other users are "
 "logged in."
 msgstr "在其他使用者登入時停止系統需要身份驗證。"
 
 #: src/login/org.freedesktop.login1.policy:246
-msgid "Halt the system while an application asked to inhibit it"
+msgid "Halt the system while an application is inhibiting this"
 msgstr "在應用程式阻止時停止系統"
 
 #: src/login/org.freedesktop.login1.policy:247
 msgid ""
-"Authentication is required for halting the system while an application asked "
+"Authentication is required to halt the system while an application asked "
 "to inhibit it."
 msgstr "在應用程式阻止時停止系統需要身份驗證。"
 
@@ -340,7 +340,7 @@ msgid "Suspend the system"
 msgstr "暫停系統"
 
 #: src/login/org.freedesktop.login1.policy:258
-msgid "Authentication is required for suspending the system."
+msgid "Authentication is required to suspend the system."
 msgstr "暫停系統需要驗證。"
 
 #: src/login/org.freedesktop.login1.policy:267
@@ -349,18 +349,18 @@ msgstr "在有其他使用者登入時暫停系統"
 
 #: src/login/org.freedesktop.login1.policy:268
 msgid ""
-"Authentication is required for suspending the system while other users are "
+"Authentication is required to suspend the system while other users are "
 "logged in."
 msgstr "在有其他使用者登入時暫停系統需要驗證。"
 
 #: src/login/org.freedesktop.login1.policy:278
-msgid "Suspend the system while an application asked to inhibit it"
+msgid "Suspend the system while an application is inhibiting this"
 msgstr "當應用程式阻止暫停系統時將系統暫停"
 
 #: src/login/org.freedesktop.login1.policy:279
 msgid ""
-"Authentication is required for suspending the system while an application "
-"asked to inhibit it."
+"Authentication is required to suspend the system while an application "
+"is inhibiting this."
 msgstr "當應用程式阻止系統暫停時將系統暫停需要驗證。"
 
 #: src/login/org.freedesktop.login1.policy:289
@@ -368,7 +368,7 @@ msgid "Hibernate the system"
 msgstr "系統冬眠"
 
 #: src/login/org.freedesktop.login1.policy:290
-msgid "Authentication is required for hibernating the system."
+msgid "Authentication is required to hibernate the system."
 msgstr "系統冬眠需要驗證。"
 
 #: src/login/org.freedesktop.login1.policy:299
@@ -377,18 +377,18 @@ msgstr "在有其他使用者登入時冬眠系統"
 
 #: src/login/org.freedesktop.login1.policy:300
 msgid ""
-"Authentication is required for hibernating the system while other users are "
+"Authentication is required to hibernate the system while other users are "
 "logged in."
 msgstr "在有其他使用者登入時冬眠系統需要驗證。"
 
 #: src/login/org.freedesktop.login1.policy:310
-msgid "Hibernate the system while an application asked to inhibit it"
+msgid "Hibernate the system while an application is inhibiting this"
 msgstr "當應用程式阻止冬眠系統時將系統冬眠"
 
 #: src/login/org.freedesktop.login1.policy:311
 msgid ""
-"Authentication is required for hibernating the system while an application "
-"asked to inhibit it."
+"Authentication is required to hibernate the system while an application "
+"is inhibiting this."
 msgstr "當應用程式阻止系統冬眠時將系統冬眠需要驗證。"
 
 #: src/login/org.freedesktop.login1.policy:321
@@ -397,7 +397,7 @@ msgstr "管理活躍的工作階段、使用者與座位"
 
 #: src/login/org.freedesktop.login1.policy:322
 msgid ""
-"Authentication is required for managing active sessions, users and seats."
+"Authentication is required to manage active sessions, users and seats."
 msgstr "管理活躍的工作階段、使用者與座位需要驗證。"
 
 #: src/login/org.freedesktop.login1.policy:331
index 11960e54236a42ca309298fae17cc70212a50508..dc9d02f0b937ac7cb145e79915154ed441d46667 100644 (file)
@@ -7,8 +7,9 @@
 #  the Free Software Foundation; either version 2.1 of the License, or
 #  (at your option) any later version.
 
-# These ones should be enabled by default, even if distributions
-# generally follow a default-off policy.
+# Settings for systemd units distributed with systemd itself. Most of these
+# should be enabled by default, even if the distribution follows a general
+# default-off policy.
 
 enable remote-fs.target
 enable remote-cryptsetup.target
@@ -34,3 +35,20 @@ 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 22fe41fc337937ac0c34c65125263923cd62be16..fd402c8c11cf04fb01c6979e8c3113dd5e4e6feb 100644 (file)
@@ -7,8 +7,15 @@
 #  the Free Software Foundation; either version 2.1 of the License, or
 #  (at your option) any later version.
 
-# These ones should be enabled by default, even if distributions
-# generally follow a default-off policy.
+# Settings for systemd units distributed with systemd itself. These should be
+# enabled by default, even if the distribution follows a general default-off
+# policy.
 
 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 e5e608acd3daa4a73990a1ce8e17aa8842f46ffd..055a62a12d75c094ba1e9c71b58e89a9d259ec18 100644 (file)
@@ -5,19 +5,21 @@ KERNEL!="event*", GOTO="evdev_end"
 
 # skip later rules when we find something for this input device
 IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=evdev:", \
-  RUN{builtin}+="keyboard", GOTO="evdev_end"
+  IMPORT{builtin}="keyboard", GOTO="evdev_end"
 
 # AT keyboard matching by the machine's DMI data
 DRIVERS=="atkbd", \
   IMPORT{builtin}="hwdb 'evdev:atkbd:$attr{[dmi/id]modalias}'", \
-  RUN{builtin}+="keyboard", GOTO="evdev_end"
+  IMPORT{builtin}="keyboard", GOTO="evdev_end"
 
 # device matching the input device name + properties + the machine's DMI data
-KERNELS=="input*", IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:phys:$attr{phys}:ev:$attr{capabilities/ev}:$attr{[dmi/id]modalias}'", \
-  RUN{builtin}+="keyboard", GOTO="evdev_end"
+KERNELS=="input*", \
+  IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:phys:$attr{phys}:ev:$attr{capabilities/ev}:$attr{[dmi/id]modalias}'", \
+  IMPORT{builtin}="keyboard", GOTO="evdev_end"
 
 # device matching the input device name and the machine's DMI data
-KERNELS=="input*", IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:$attr{[dmi/id]modalias}'", \
-  RUN{builtin}+="keyboard", GOTO="evdev_end"
+KERNELS=="input*", \
+  IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:$attr{[dmi/id]modalias}'", \
+  IMPORT{builtin}="keyboard", GOTO="evdev_end"
 
 LABEL="evdev_end"
index 504ada59ee0daee3a87e35e66f5840c4c3dfcb05..2bf8ce0d52eaea880c965696472f271b3b91de37 100755 (executable)
@@ -35,6 +35,8 @@ while [ -z "\$(ip route list 0/0)" ]; do sleep 1; done
 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 purge --auto-remove -y unattended-upgrades
 systemctl unmask systemd-networkd
 systemctl enable systemd-networkd
index b60d8c5c4bfa6678a108f6c74fd68fac2ad2ab34..0b84d5c83487b5702418279d4c923c4d8ca6c021 100644 (file)
@@ -34,7 +34,7 @@ _portablectl() {
     local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
     local -A OPTS=(
         [STANDALONE]='-q --quiet --runtime --no-reload --cat --no-pager --no-legend
-                              --no-ask-password -h --help --version'
+                              --no-ask-password --enable --now -h --help --version'
         [ARG]='-p --profile --copy -H --host -M --machine'
     )
 
index 7fa17c99f52e0123d8344419f929dc02dd4f3564..d681251c0460ed669a1e9fab14cbc5a76bd31e71 100644 (file)
@@ -65,6 +65,7 @@ struct security_info {
         bool protect_kernel_modules;
         bool protect_kernel_tunables;
         bool protect_kernel_logs;
+        bool protect_clock;
 
         char *protect_home;
         char *protect_system;
@@ -746,7 +747,7 @@ static const struct security_assessor security_assessor_table[] = {
         {
                 .id = "ProtectControlGroups=",
                 .description_good = "Service cannot modify the control group file system",
-                .description_bad = "Service may modify to the control group file system",
+                .description_bad = "Service may modify the control group file system",
                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectControlGroups=",
                 .weight = 1000,
                 .range = 1,
@@ -783,6 +784,16 @@ static const struct security_assessor security_assessor_table[] = {
                 .assess = assess_bool,
                 .offset = offsetof(struct security_info, protect_kernel_logs),
         },
+        {
+                .id = "ProtectClock=",
+                .description_good = "Service cannot write to the hardware clock or system clock",
+                .description_bad = "Service may write to the hardware clock or system clock",
+                .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectClock=",
+                .weight = 1000,
+                .range = 1,
+                .assess = assess_bool,
+                .offset = offsetof(struct security_info, protect_clock),
+        },
         {
                 .id = "ProtectHome=",
                 .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHome=",
@@ -1447,11 +1458,11 @@ static int assess(const struct security_info *info, Table *overview_table, Analy
                 if (!details_table)
                         return log_oom();
 
-                (void) table_set_sort(details_table, 3, 1, (size_t) -1);
+                (void) table_set_sort(details_table, (size_t) 3, (size_t) 1, (size_t) -1);
                 (void) table_set_reverse(details_table, 3, true);
 
                 if (getenv_bool("SYSTEMD_ANALYZE_DEBUG") <= 0)
-                        (void) table_set_display(details_table, 0, 1, 2, 6, (size_t) -1);
+                        (void) table_set_display(details_table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 6, (size_t) -1);
         }
 
         for (i = 0; i < ELEMENTSOF(security_assessor_table); i++) {
@@ -1907,6 +1918,7 @@ static int acquire_security_info(sd_bus *bus, const char *name, struct security_
                 { "ProtectKernelModules",    "b",       NULL,                                    offsetof(struct security_info, protect_kernel_modules)    },
                 { "ProtectKernelTunables",   "b",       NULL,                                    offsetof(struct security_info, protect_kernel_tunables)   },
                 { "ProtectKernelLogs",       "b",       NULL,                                    offsetof(struct security_info, protect_kernel_logs)       },
+                { "ProtectClock",            "b",       NULL,                                    offsetof(struct security_info, protect_clock)             },
                 { "ProtectSystem",           "s",       NULL,                                    offsetof(struct security_info, protect_system)            },
                 { "RemoveIPC",               "b",       NULL,                                    offsetof(struct security_info, remove_ipc)                },
                 { "RestrictAddressFamilies", "(bas)",   property_read_restrict_address_families, 0                                                         },
@@ -1984,6 +1996,10 @@ static int acquire_security_info(sd_bus *bus, const char *name, struct security_
         if (info->protect_kernel_logs)
                 info->capability_bounding_set &= ~(UINT64_C(1) << CAP_SYSLOG);
 
+        if (info->protect_clock)
+                info->capability_bounding_set &= ~((UINT64_C(1) << CAP_SYS_TIME) |
+                                                   (UINT64_C(1) << CAP_WAKE_ALARM));
+
         if (info->private_devices)
                 info->capability_bounding_set &= ~((UINT64_C(1) << CAP_MKNOD) |
                                                    (UINT64_C(1) << CAP_SYS_RAWIO));
index 703a501ae6849ae208068873f49e710693465bd1..3ea9041c188c3403e928a1e105443d0c57b77153 100644 (file)
@@ -1101,7 +1101,7 @@ static int analyze_blame(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
-        r = table_set_sort(table, 0, SIZE_MAX);
+        r = table_set_sort(table, (size_t) 0, (size_t) SIZE_MAX);
         if (r < 0)
                 return r;
 
@@ -1114,7 +1114,7 @@ static int analyze_blame(int argc, char *argv[], void *userdata) {
                         continue;
 
                 r = table_add_many(table,
-                                   TABLE_TIMESPAN_MSEC, &u->time,
+                                   TABLE_TIMESPAN_MSEC, u->time,
                                    TABLE_STRING, u->name);
                 if (r < 0)
                         return table_log_add_error(r);
@@ -1160,9 +1160,9 @@ static int graph_one_property(
         assert(prop);
         assert(color);
 
-        match_patterns = strv_fnmatch(patterns, u->id, 0);
+        match_patterns = strv_fnmatch(patterns, u->id);
 
-        if (!strv_isempty(from_patterns) && !match_patterns && !strv_fnmatch(from_patterns, u->id, 0))
+        if (!strv_isempty(from_patterns) && !match_patterns && !strv_fnmatch(from_patterns, u->id))
                 return 0;
 
         r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
@@ -1172,9 +1172,9 @@ static int graph_one_property(
         STRV_FOREACH(unit, units) {
                 bool match_patterns2;
 
-                match_patterns2 = strv_fnmatch(patterns, *unit, 0);
+                match_patterns2 = strv_fnmatch(patterns, *unit);
 
-                if (!strv_isempty(to_patterns) && !match_patterns2 && !strv_fnmatch(to_patterns, *unit, 0))
+                if (!strv_isempty(to_patterns) && !match_patterns2 && !strv_fnmatch(to_patterns, *unit))
                         continue;
 
                 if (!strv_isempty(patterns) && !match_patterns && !match_patterns2)
@@ -1862,9 +1862,9 @@ static int dump_timespan(int argc, char *argv[], void *userdata) {
                         return table_log_add_error(r);
 
                 r = table_add_many(table,
-                                   TABLE_UINT64, &output_usecs,
+                                   TABLE_UINT64, output_usecs,
                                    TABLE_STRING, "Human:",
-                                   TABLE_TIMESPAN, &output_usecs,
+                                   TABLE_TIMESPAN, output_usecs,
                                    TABLE_SET_COLOR, ansi_highlight());
                 if (r < 0)
                         return table_log_add_error(r);
@@ -1917,7 +1917,7 @@ static int test_timestamp_one(const char *p) {
                            TABLE_STRING, "Original form:",
                            TABLE_STRING, p,
                            TABLE_STRING, "Normalized form:",
-                           TABLE_TIMESTAMP, &usec,
+                           TABLE_TIMESTAMP, usec,
                            TABLE_SET_COLOR, ansi_highlight_blue());
         if (r < 0)
                 return table_log_add_error(r);
@@ -1925,7 +1925,7 @@ static int test_timestamp_one(const char *p) {
         if (!in_utc_timezone()) {
                 r = table_add_many(table,
                                    TABLE_STRING, "(in UTC):",
-                                   TABLE_TIMESTAMP_UTC, &usec);
+                                   TABLE_TIMESTAMP_UTC, usec);
                 if (r < 0)
                         return table_log_add_error(r);
         }
@@ -1946,7 +1946,7 @@ static int test_timestamp_one(const char *p) {
 
         r = table_add_many(table,
                            TABLE_STRING, "From now:",
-                           TABLE_TIMESTAMP_RELATIVE, &usec);
+                           TABLE_TIMESTAMP_RELATIVE, usec);
         if (r < 0)
                 return table_log_add_error(r);
 
@@ -2042,7 +2042,7 @@ static int test_calendar_one(usec_t n, const char *p) {
                 if (i == 0) {
                         r = table_add_many(table,
                                            TABLE_STRING, "Next elapse:",
-                                           TABLE_TIMESTAMP, &next,
+                                           TABLE_TIMESTAMP, next,
                                            TABLE_SET_COLOR, ansi_highlight_blue());
                         if (r < 0)
                                 return table_log_add_error(r);
@@ -2059,7 +2059,7 @@ static int test_calendar_one(usec_t n, const char *p) {
                                 return table_log_add_error(r);
 
                         r = table_add_many(table,
-                                           TABLE_TIMESTAMP, &next,
+                                           TABLE_TIMESTAMP, next,
                                            TABLE_SET_COLOR, ansi_highlight_blue());
                         if (r < 0)
                                 return table_log_add_error(r);
@@ -2068,14 +2068,14 @@ static int test_calendar_one(usec_t n, const char *p) {
                 if (!in_utc_timezone()) {
                         r = table_add_many(table,
                                            TABLE_STRING, "(in UTC):",
-                                           TABLE_TIMESTAMP_UTC, &next);
+                                           TABLE_TIMESTAMP_UTC, next);
                         if (r < 0)
                                 return table_log_add_error(r);
                 }
 
                 r = table_add_many(table,
                                    TABLE_STRING, "From now:",
-                                   TABLE_TIMESTAMP_RELATIVE, &next);
+                                   TABLE_TIMESTAMP_RELATIVE, next);
                 if (r < 0)
                         return table_log_add_error(r);
 
index ea43abd7b3f63ca7d8342197ae4c20276df45270..502c3a0c4448aa54565674e7372bd660ec8c43f7 100644 (file)
@@ -20,6 +20,7 @@
 #include "strv.h"
 #include "time-util.h"
 #include "utf8.h"
+#include "virt.h"
 
 #if ENABLE_EFI
 
@@ -221,6 +222,41 @@ int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *v)
         return efi_set_variable(vendor, name, u16, (char16_strlen(u16) + 1) * sizeof(char16_t));
 }
 
+bool is_efi_boot(void) {
+        if (detect_container() > 0)
+                return false;
+
+        return access("/sys/firmware/efi/", F_OK) >= 0;
+}
+
+static int read_flag(const char *varname) {
+        _cleanup_free_ void *v = NULL;
+        uint8_t b;
+        size_t s;
+        int r;
+
+        if (!is_efi_boot()) /* If this is not an EFI boot, assume the queried flags are zero */
+                return 0;
+
+        r = efi_get_variable(EFI_VENDOR_GLOBAL, varname, NULL, &v, &s);
+        if (r < 0)
+                return r;
+
+        if (s != 1)
+                return -EINVAL;
+
+        b = *(uint8_t *)v;
+        return !!b;
+}
+
+bool is_efi_secure_boot(void) {
+        return read_flag("SecureBoot") > 0;
+}
+
+bool is_efi_secure_boot_setup_mode(void) {
+        return read_flag("SetupMode") > 0;
+}
+
 int systemd_efi_options_variable(char **line) {
         const char *e;
         int r;
index 46ca58d0a52fd2dec3119f44fbd1b2a431fbe627..13a33c66053de6c5446598b38b9761d32e9dd201 100644 (file)
@@ -28,6 +28,10 @@ int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p);
 int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size);
 int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *p);
 
+bool is_efi_boot(void);
+bool is_efi_secure_boot(void);
+bool is_efi_secure_boot_setup_mode(void);
+
 int systemd_efi_options_variable(char **line);
 
 #else
@@ -52,6 +56,18 @@ static inline int efi_set_variable_string(sd_id128_t vendor, const char *name, c
         return -EOPNOTSUPP;
 }
 
+static inline bool is_efi_boot(void) {
+        return false;
+}
+
+static inline bool is_efi_secure_boot(void) {
+        return false;
+}
+
+static inline bool is_efi_secure_boot_setup_mode(void) {
+        return false;
+}
+
 static inline int systemd_efi_options_variable(char **line) {
         return -ENODATA;
 }
index 33a6f204f55f5efb92e673c752cfac3f72a27d91..c5c44d2e7d8e940b087edf28e10e7c69031745e8 100644 (file)
@@ -102,7 +102,7 @@ char *cescape(const char *s) {
         return cescape_length(s, strlen(s));
 }
 
-int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit) {
+int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul) {
         int r = 1;
 
         assert(p);
@@ -171,7 +171,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit)
                         return -EINVAL;
 
                 /* Don't allow NUL bytes */
-                if (a == 0 && b == 0)
+                if (a == 0 && b == 0 && !accept_nul)
                         return -EINVAL;
 
                 *ret = (a << 4U) | b;
@@ -199,7 +199,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit)
                 c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3];
 
                 /* Don't allow 0 chars */
-                if (c == 0)
+                if (c == 0 && !accept_nul)
                         return -EINVAL;
 
                 *ret = c;
@@ -227,7 +227,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit)
                     ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] <<  8U) | ((uint32_t) a[6] <<  4U) |  (uint32_t) a[7];
 
                 /* Don't allow 0 chars */
-                if (c == 0)
+                if (c == 0 && !accept_nul)
                         return -EINVAL;
 
                 /* Don't allow invalid code points */
@@ -267,7 +267,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit)
                         return -EINVAL;
 
                 /* don't allow NUL bytes */
-                if (a == 0 && b == 0 && c == 0)
+                if (a == 0 && b == 0 && c == 0 && !accept_nul)
                         return -EINVAL;
 
                 /* Don't allow bytes above 255 */
@@ -333,7 +333,7 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
                         return -EINVAL;
                 }
 
-                k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit);
+                k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit, flags & UNESCAPE_ACCEPT_NUL);
                 if (k < 0) {
                         if (flags & UNESCAPE_RELAX) {
                                 /* Invalid escape code, let's take it literal then */
@@ -360,14 +360,6 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
         return t - r;
 }
 
-int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
-        return cunescape_length_with_prefix(s, length, NULL, flags, ret);
-}
-
-int cunescape(const char *s, UnescapeFlags flags, char **ret) {
-        return cunescape_length(s, strlen(s), flags, ret);
-}
-
 char *xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits) {
         char *ans, *t, *prev, *prev2;
         const char *f;
index b26054c5df8abb765e7c68163c3c47ca41906c22..b8eb137c3d398bf4e6adf48c33d3f7721eaac7d4 100644 (file)
 #define SHELL_NEED_ESCAPE_POSIX "\\\'"
 
 typedef enum UnescapeFlags {
-        UNESCAPE_RELAX = 1,
+        UNESCAPE_RELAX      = 1 << 0,
+        UNESCAPE_ACCEPT_NUL = 1 << 1,
 } UnescapeFlags;
 
 typedef enum EscapeStyle {
         ESCAPE_BACKSLASH = 1,
-        ESCAPE_POSIX = 2,
+        ESCAPE_POSIX     = 2,
 } EscapeStyle;
 
 char *cescape(const char *s);
 char *cescape_length(const char *s, size_t n);
 int cescape_char(char c, char *buf);
 
-int cunescape(const char *s, UnescapeFlags flags, char **ret);
-int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret);
 int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
-int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit);
+static inline int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
+        return cunescape_length_with_prefix(s, length, NULL, flags, ret);
+}
+static inline int cunescape(const char *s, UnescapeFlags flags, char **ret) {
+        return cunescape_length(s, strlen(s), flags, ret);
+}
+int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul);
 
 char *xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits);
 static inline char *xescape(const char *s, const char *bad) {
index d7c215cb48e7a267e4bc5f3e2f28d19d62aaeec9..ac9bf6099d87f62a78f9d9f9c7d01475db97121e 100644 (file)
@@ -90,7 +90,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
                                 bool eight_bit = false;
                                 char32_t u;
 
-                                r = cunescape_one(*p, (size_t) -1, &u, &eight_bit);
+                                r = cunescape_one(*p, (size_t) -1, &u, &eight_bit, false);
                                 if (r < 0) {
                                         if (flags & EXTRACT_CUNESCAPE_RELAX) {
                                                 s[sz++] = '\\';
index 59622508a333624a602895577361b76a2397ec0c..c47fa76ea8ff2e709d1b8d92b92fe0aed0609cf8 100644 (file)
@@ -5,30 +5,18 @@
 #include <net/if.h>
 #include <stdbool.h>
 
-#if SIZEOF_PID_T == 4
-#  define PID_PRI PRIi32
-#elif SIZEOF_PID_T == 2
-#  define PID_PRI PRIi16
-#else
-#  error Unknown pid_t size
-#endif
+#include "cgroup-util.h"
+#include "macro.h"
+
+assert_cc(sizeof(pid_t) == sizeof(int32_t));
+#define PID_PRI PRIi32
 #define PID_FMT "%" PID_PRI
 
-#if SIZEOF_UID_T == 4
-#  define UID_FMT "%" PRIu32
-#elif SIZEOF_UID_T == 2
-#  define UID_FMT "%" PRIu16
-#else
-#  error Unknown uid_t size
-#endif
+assert_cc(sizeof(uid_t) == sizeof(uint32_t));
+#define UID_FMT "%" PRIu32
 
-#if SIZEOF_GID_T == 4
-#  define GID_FMT "%" PRIu32
-#elif SIZEOF_GID_T == 2
-#  define GID_FMT "%" PRIu16
-#else
-#  error Unknown gid_t size
-#endif
+assert_cc(sizeof(gid_t) == sizeof(uint32_t));
+#define GID_FMT "%" PRIu32
 
 #if SIZEOF_TIME_T == 8
 #  define PRI_TIME PRIi64
@@ -84,8 +72,15 @@ typedef enum {
         FORMAT_BYTES_TRAILING_B  = 1 << 2,
 } FormatBytesFlag;
 
-#define FORMAT_BYTES_MAX 8
+#define FORMAT_BYTES_MAX 16
 char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag);
 static inline char *format_bytes(char *buf, size_t l, uint64_t t) {
         return format_bytes_full(buf, l, t, FORMAT_BYTES_USE_IEC | FORMAT_BYTES_BELOW_POINT | FORMAT_BYTES_TRAILING_B);
 }
+static inline char *format_bytes_cgroup_protection(char *buf, size_t l, uint64_t t) {
+        if (t == CGROUP_LIMIT_MAX) {
+                (void) snprintf(buf, l, "%s", "infinity");
+                return buf;
+        }
+        return format_bytes(buf, l, t);
+}
index 5723c845e436285ec6f5b01a2e0fe4051789feaf..558cafbcaf53a2b2e62f1be1dea453a44d31a3f4 100644 (file)
@@ -272,6 +272,52 @@ int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
         return do_chown || do_chmod;
 }
 
+int chmod_and_chown_unsafe(const char *path, mode_t mode, uid_t uid, gid_t gid) {
+        bool do_chown, do_chmod;
+        struct stat st;
+
+        assert(path);
+
+        /* Change ownership and access mode of the specified path, see description of fchmod_and_chown().
+         * Should only be used on trusted paths. */
+
+        if (lstat(path, &st) < 0)
+                return -errno;
+
+        do_chown =
+                (uid != UID_INVALID && st.st_uid != uid) ||
+                (gid != GID_INVALID && st.st_gid != gid);
+
+        do_chmod =
+                !S_ISLNK(st.st_mode) && /* chmod is not defined on symlinks */
+                ((mode != MODE_INVALID && ((st.st_mode ^ mode) & 07777) != 0) ||
+                 do_chown); /* If we change ownership, make sure we reset the mode afterwards, since chown()
+                             * modifies the access mode too */
+
+        if (mode == MODE_INVALID)
+                mode = st.st_mode; /* If we only shall do a chown(), save original mode, since chown() might break it. */
+        else if ((mode & S_IFMT) != 0 && ((mode ^ st.st_mode) & S_IFMT) != 0)
+                return -EINVAL; /* insist on the right file type if it was specified */
+
+        if (do_chown && do_chmod) {
+                mode_t minimal = st.st_mode & mode; /* the subset of the old and the new mask */
+
+                if (((minimal ^ st.st_mode) & 07777) != 0)
+                        if (chmod(path, minimal & 07777) < 0)
+                                return -errno;
+        }
+
+        if (do_chown)
+                if (lchown(path, uid, gid) < 0)
+                        return -errno;
+
+        if (do_chmod)
+                if (chmod(path, mode & 07777) < 0)
+                        return -errno;
+
+        return do_chown || do_chmod;
+}
+
 int fchmod_umask(int fd, mode_t m) {
         mode_t u;
         int r;
@@ -797,6 +843,14 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                 if (r < 0)
                         return r;
 
+                /* Simplify the root directory, so that it has no duplicate slashes and nothing at the
+                 * end. While we won't resolve the root path we still simplify it. Note that dropping the
+                 * trailing slash should not change behaviour, since when opening it we specify O_DIRECTORY
+                 * anyway. Moreover at the end of this function after processing everything we'll always turn
+                 * the empty string back to "/". */
+                delete_trailing_chars(root, "/");
+                path_simplify(root, true);
+
                 if (flags & CHASE_PREFIX_ROOT) {
                         /* We don't support relative paths in combination with a root directory */
                         if (!path_is_absolute(path))
@@ -810,7 +864,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
         if (r < 0)
                 return r;
 
-        fd = open("/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
+        fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH);
         if (fd < 0)
                 return -errno;
 
@@ -819,6 +873,31 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                         return -errno;
         }
 
+        if (root) {
+                _cleanup_free_ char *absolute = NULL;
+                const char *e;
+
+                /* If we are operating on a root directory, let's take the root directory as it is. */
+
+                e = path_startswith(buffer, root);
+                if (!e)
+                        return log_full_errno(flags & CHASE_WARN ? LOG_WARNING : LOG_DEBUG,
+                                              SYNTHETIC_ERRNO(ECHRNG),
+                                              "Specified path '%s' is outside of specified root directory '%s', refusing to resolve.",
+                                              path, root);
+
+                done = strdup(root);
+                if (!done)
+                        return -ENOMEM;
+
+                /* Make sure "todo" starts with a slash */
+                absolute = strjoin("/", e);
+                if (!absolute)
+                        return -ENOMEM;
+
+                free_and_replace(buffer, absolute);
+        }
+
         todo = buffer;
         for (;;) {
                 _cleanup_free_ char *first = NULL;
@@ -828,6 +907,15 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
 
                 /* Determine length of first component in the path */
                 n = strspn(todo, "/");                  /* The slashes */
+
+                if (n > 1) {
+                        /* If we are looking at more than a single slash then skip all but one, so that when
+                         * we are done with everything we have a normalized path with only single slashes
+                         * separating the path components. */
+                        todo += n - 1;
+                        n = 1;
+                }
+
                 m = n + strcspn(todo + n, "/");         /* The entire length of the component */
 
                 /* Extract the first component. */
@@ -930,7 +1018,6 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                 if (fstat(child, &st) < 0)
                         return -errno;
                 if ((flags & CHASE_SAFE) &&
-                    (empty_or_root(root) || (size_t)(todo - buffer) > strlen(root)) &&
                     unsafe_transition(&previous_stat, &st))
                         return log_unsafe_transition(fd, child, path, flags);
 
@@ -961,7 +1048,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                                  * directory as base. */
 
                                 safe_close(fd);
-                                fd = open(root ?: "/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
+                                fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH);
                                 if (fd < 0)
                                         return -errno;
 
index 78d68be9fd855ba2396e4b93abec8ed0b6efd265..6b9ade2ec15e3c21e90511ccf86458b8b7473df6 100644 (file)
@@ -34,6 +34,7 @@ int readlink_and_make_absolute(const char *p, char **r);
 
 int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
 int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid);
+int chmod_and_chown_unsafe(const char *path, mode_t mode, uid_t uid, gid_t gid);
 
 int fchmod_umask(int fd, mode_t mode);
 int fchmod_opath(int fd, mode_t m);
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 */
index b2320701938b3e62a9394d7452e2190239af5454..d272ffd29680335578316de4404ed40d2255834e 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 set -eu
 
index dc625119355f7c3e042f32d47d8acf05100f7bd3..96151ffbf8cc5c13058f0e0149cb8e60a9946f93 100644 (file)
@@ -345,6 +345,9 @@ const char *special_glyph(SpecialGlyph code) {
                         [SPECIAL_GLYPH_MU]                      = "u",
                         [SPECIAL_GLYPH_CHECK_MARK]              = "+",
                         [SPECIAL_GLYPH_CROSS_MARK]              = "-",
+                        [SPECIAL_GLYPH_LIGHT_SHADE]             = "-",
+                        [SPECIAL_GLYPH_DARK_SHADE]              = "X",
+                        [SPECIAL_GLYPH_SIGMA]                   = "S",
                         [SPECIAL_GLYPH_ARROW]                   = "->",
                         [SPECIAL_GLYPH_ELLIPSIS]                = "...",
                         [SPECIAL_GLYPH_ECSTATIC_SMILEY]         = ":-]",
@@ -371,6 +374,9 @@ const char *special_glyph(SpecialGlyph code) {
                         [SPECIAL_GLYPH_MU]                      = "\316\274",                 /* μ (actually called: GREEK SMALL LETTER MU) */
                         [SPECIAL_GLYPH_CHECK_MARK]              = "\342\234\223",             /* ✓ */
                         [SPECIAL_GLYPH_CROSS_MARK]              = "\342\234\227",             /* ✗ (actually called: BALLOT X) */
+                        [SPECIAL_GLYPH_LIGHT_SHADE]             = "\342\226\221",             /* ░ */
+                        [SPECIAL_GLYPH_DARK_SHADE]              = "\342\226\223",             /* ▒ */
+                        [SPECIAL_GLYPH_SIGMA]                   = "\316\243",                 /* Σ */
 
                         /* Single glyph in Unicode, two in ASCII */
                         [SPECIAL_GLYPH_ARROW]                   = "\342\206\222",             /* → (actually called: RIGHTWARDS ARROW) */
index 1df8ac4cb0ce631a78a0ecea9da113976dd5dafa..cefc4e7f0ed0ea1493b8f6ed881b8b427efe71e2 100644 (file)
@@ -51,6 +51,9 @@ typedef enum {
         SPECIAL_GLYPH_CROSS_MARK,
         SPECIAL_GLYPH_ARROW,
         SPECIAL_GLYPH_ELLIPSIS,
+        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_HAPPY_SMILEY,
index 2045175d1cb3c733d8a7ce6e47c0778e8df0dc38..29cf22676ae6f3e6e2416d6c4b802dbb8ebe7fb0 100644 (file)
@@ -139,6 +139,38 @@ enum nss_status _nss_##module##_getgrgid_r(             \
                 char *buffer, size_t buflen,            \
                 int *errnop) _public_
 
+#define NSS_PWENT_PROTOTYPES(module)                    \
+enum nss_status _nss_##module##_endpwent(               \
+                void) _public_;                         \
+enum nss_status _nss_##module##_setpwent(               \
+                int stayopen) _public_;                 \
+enum nss_status _nss_##module##_getpwent_r(             \
+                struct passwd *result,                  \
+                char *buffer,                           \
+                size_t buflen,                          \
+                int *errnop) _public_;
+
+#define NSS_GRENT_PROTOTYPES(module)                    \
+enum nss_status _nss_##module##_endgrent(               \
+                void) _public_;                         \
+enum nss_status _nss_##module##_setgrent(               \
+                int stayopen) _public_;                 \
+enum nss_status _nss_##module##_getgrent_r(             \
+                struct group *result,                   \
+                char *buffer,                           \
+                size_t buflen,                          \
+                int *errnop) _public_;
+
+#define NSS_INITGROUPS_PROTOTYPE(module)                \
+enum nss_status _nss_##module##_initgroups_dyn(         \
+                const char *user,                       \
+                gid_t group,                            \
+                long int *start,                        \
+                long int *size,                         \
+                gid_t **groupsp,                        \
+                long int limit,                         \
+                int *errnop) _public_;
+
 typedef enum nss_status (*_nss_gethostbyname4_r_t)(
                 const char *name,
                 struct gaih_addrtuple **pat,
index b644c4a4d6043eb51069c55e112d93158d69cc13..e0094b0f370a8943774b8ef5066420990441487a 100644 (file)
@@ -698,6 +698,22 @@ int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) {
         return 0;
 }
 
+int parse_ip_prefix_length(const char *s, int *ret) {
+        unsigned l;
+        int r;
+
+        r = safe_atou(s, &l);
+        if (r < 0)
+                return r;
+
+        if (l > 128)
+                return -ERANGE;
+
+        *ret = (int) l;
+
+        return 0;
+}
+
 int parse_dev(const char *s, dev_t *ret) {
         const char *major;
         unsigned x, y;
index c6d1d249672222eed0a5c0e52a1d41a05e86d8b5..36d76ba57617b5f98bc9c4a371e2593881935655 100644 (file)
@@ -45,9 +45,13 @@ static inline int safe_atoux16(const char *s, uint16_t *ret) {
 
 int safe_atoi16(const char *s, int16_t *ret);
 
-static inline int safe_atou32(const char *s, uint32_t *ret_u) {
+static inline int safe_atou32_full(const char *s, unsigned base, uint32_t *ret_u) {
         assert_cc(sizeof(uint32_t) == sizeof(unsigned));
-        return safe_atou(s, (unsigned*) ret_u);
+        return safe_atou_full(s, base, (unsigned*) ret_u);
+}
+
+static inline int safe_atou32(const char *s, uint32_t *ret_u) {
+        return safe_atou32_full(s, 0, (unsigned*) ret_u);
 }
 
 static inline int safe_atoi32(const char *s, int32_t *ret_i) {
@@ -112,4 +116,6 @@ int parse_nice(const char *p, int *ret);
 int parse_ip_port(const char *s, uint16_t *ret);
 int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high);
 
+int parse_ip_prefix_length(const char *s, int *ret);
+
 int parse_oom_score_adjust(const char *s, int *ret);
index 49a211a527d2b01aa7f3b2df914dbf233cd3534a..ba13de01ff0b194f6cc9925fe0fcd6e7078458d4 100644 (file)
@@ -1125,3 +1125,19 @@ bool path_strv_contains(char **l, const char *path) {
 
         return false;
 }
+
+bool prefixed_path_strv_contains(char **l, const char *path) {
+        char **i, *j;
+
+        STRV_FOREACH(i, l) {
+                j = *i;
+                if (*j == '-')
+                        j++;
+                if (*j == '+')
+                        j++;
+                if (path_equal(j, path))
+                        return true;
+        }
+
+        return false;
+}
index f49a876f3d22d6324b578fb61154f10d695f2911..30031fca8ef52274215af234306c8020b2f9f2c2 100644 (file)
@@ -173,3 +173,4 @@ static inline const char *empty_to_root(const char *path) {
 }
 
 bool path_strv_contains(char **l, const char *path);
+bool prefixed_path_strv_contains(char **l, const char *path);
index d3d99d9a7f90228222eaa09de6b778a048b8a95c..1af58717c6868f514cc07df7d744a4b1a39954fb 100644 (file)
@@ -39,6 +39,18 @@ int proc_cmdline(char **ret) {
                 return read_one_line_file("/proc/cmdline", ret);
 }
 
+/* In SecureBoot mode this is probably not what you want. As your cmdline is
+ * cryptographically signed like when using Type #2 EFI Unified Kernel Images
+ * (https://systemd.io/BOOT_LOADER_SPECIFICATION/) The user's intention is then
+ * that the cmdline should not be modified.  You want to make sure that the
+ * system starts up as exactly specified in the signed artifact. */
+static int systemd_options_variable(char **line) {
+        if (is_efi_secure_boot())
+                return -ENODATA;
+
+        return systemd_efi_options_variable(line);
+}
+
 static int proc_cmdline_extract_first(const char **p, char **ret_word, ProcCmdlineFlags flags) {
         const char *q = *p;
         int r;
@@ -119,7 +131,7 @@ int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, ProcCmdlineF
 
         /* We parse the EFI variable first, because later settings have higher priority. */
 
-        r = systemd_efi_options_variable(&line);
+        r = systemd_options_variable(&line);
         if (r < 0 && r != -ENODATA)
                 log_debug_errno(r, "Failed to get SystemdOptions EFI variable, ignoring: %m");
 
@@ -250,7 +262,7 @@ int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_val
                 return r;
 
         line = mfree(line);
-        r = systemd_efi_options_variable(&line);
+        r = systemd_options_variable(&line);
         if (r == -ENODATA)
                 return false; /* Not found */
         if (r < 0)
index a238b25796a2e67510e5ec25bec4912379fda13a..4160af45ba7bcfef5bf9432180c54a29268a22ec 100644 (file)
@@ -172,7 +172,6 @@ int fork_agent(const char *name, const int except[], size_t n_except, pid_t *pid
 
 int set_oom_score_adjust(int value);
 
-#if SIZEOF_PID_T == 4
 /* The highest possibly (theoretic) pid_t value on this architecture. */
 #define PID_T_MAX ((pid_t) INT32_MAX)
 /* The maximum number of concurrent processes Linux allows on this architecture, as well as the highest valid PID value
@@ -182,12 +181,6 @@ int set_oom_score_adjust(int value);
  * these values are documented in proc(5) we feel quite confident that they are stable enough for the near future at
  * least to define them here too. */
 #define TASKS_MAX 4194303U
-#elif SIZEOF_PID_T == 2
-#define PID_T_MAX ((pid_t) INT16_MAX)
-#define TASKS_MAX 32767U
-#else
-#error "Unknown pid_t size"
-#endif
 
 assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX);
 
index 2c6d407295b5a308bd194f3440e05ea6fe703394..2b5f757134ff249c3aae3179ef7cebe124100dc2 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>
 #include "time-util.h"
 
 #if HAVE_SELINUX
-DEFINE_TRIVIAL_CLEANUP_FUNC(char*, freecon);
 DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free);
-
-#define _cleanup_freecon_ _cleanup_(freeconp)
 #define _cleanup_context_free_ _cleanup_(context_freep)
 
+static int mac_selinux_reload(int seqno);
+
 static int cached_use = -1;
+static int cached_enforcing = -1;
 static struct selabel_handle *label_hnd = NULL;
 
-#define log_enforcing(...) log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG, __VA_ARGS__)
-#define log_enforcing_errno(r, ...) log_full_errno(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG, r, __VA_ARGS__)
+#define log_enforcing(...) log_full(mac_selinux_enforcing() ? LOG_ERR : LOG_WARNING, __VA_ARGS__)
+#define log_enforcing_errno(r, ...) log_full_errno(mac_selinux_enforcing() ? LOG_ERR : LOG_WARNING, r, __VA_ARGS__)
 #endif
 
 bool mac_selinux_use(void) {
@@ -52,12 +53,37 @@ bool mac_selinux_use(void) {
 #endif
 }
 
+bool mac_selinux_enforcing(void) {
+#if HAVE_SELINUX
+        if (cached_enforcing < 0) {
+                cached_enforcing = security_getenforce();
+                if (cached_enforcing == -1) {
+                        log_error_errno(errno, "Failed to get SELinux enforced status: %m");
+                }
+        }
+
+        /* treat failure as enforced mode */
+        return (cached_enforcing != 0);
+#else
+        return false;
+#endif
+}
+
 void mac_selinux_retest(void) {
 #if HAVE_SELINUX
         cached_use = -1;
+        cached_enforcing = -1;
 #endif
 }
 
+#if HAVE_SELINUX
+static int setenforce_callback(int enforcing) {
+        cached_enforcing = enforcing;
+
+        return 0;
+}
+#endif
+
 int mac_selinux_init(void) {
         int r = 0;
 
@@ -65,6 +91,9 @@ int mac_selinux_init(void) {
         usec_t before_timestamp, after_timestamp;
         struct mallinfo before_mallinfo, after_mallinfo;
 
+        selinux_set_callback(SELINUX_CB_POLICYLOAD, (union selinux_callback) mac_selinux_reload);
+        selinux_set_callback(SELINUX_CB_SETENFORCE, (union selinux_callback) setenforce_callback);
+
         if (label_hnd)
                 return 0;
 
@@ -77,7 +106,7 @@ int mac_selinux_init(void) {
         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;
+                r = mac_selinux_enforcing() ? -errno : 0;
         } else  {
                 char timespan[FORMAT_TIMESPAN_MAX];
                 int l;
@@ -107,13 +136,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);
 
@@ -124,8 +152,10 @@ 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) {
 
@@ -154,6 +184,9 @@ int mac_selinux_fix(const char *path, LabelFixFlags flags) {
         if (fstat(fd, &st) < 0)
                 return -errno;
 
+        /* 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, path, st.st_mode) < 0) {
                 r = -errno;
 
@@ -189,7 +222,7 @@ int mac_selinux_fix(const char *path, LabelFixFlags flags) {
 
 fail:
         log_enforcing_errno(r, "Unable to fix SELinux security context of %s: %m", path);
-        if (security_getenforce() == 1)
+        if (mac_selinux_enforcing())
                 return r;
 #endif
 
@@ -207,7 +240,7 @@ int mac_selinux_apply(const char *path, const char *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)
+                if (mac_selinux_enforcing())
                         return -errno;
         }
 #endif
@@ -236,6 +269,9 @@ int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
                 return -errno;
 
         sclass = string_to_security_class("process");
+        if (sclass == 0)
+                return -ENOSYS;
+
         r = security_compute_create_raw(mycon, fcon, sclass, label);
         if (r < 0)
                 return -errno;
@@ -315,6 +351,9 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *
                 return -ENOMEM;
 
         sclass = string_to_security_class("process");
+        if (sclass == 0)
+                return -ENOSYS;
+
         r = security_compute_create_raw(mycon, fcon, sclass, label);
         if (r < 0)
                 return -errno;
@@ -346,6 +385,9 @@ 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. */
@@ -360,7 +402,7 @@ static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode)
                 log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, abspath);
         }
 
-        if (security_getenforce() > 0)
+        if (mac_selinux_enforcing())
                 return -errno;
 
         return 0;
@@ -441,7 +483,7 @@ int mac_selinux_create_socket_prepare(const char *label) {
         if (setsockcreatecon(label) < 0) {
                 log_enforcing_errno(errno, "Failed to set SELinux security context %s for sockets: %m", label);
 
-                if (security_getenforce() == 1)
+                if (mac_selinux_enforcing())
                         return -errno;
         }
 #endif
@@ -494,6 +536,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 {
@@ -512,13 +557,13 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
                         goto skipped;
 
                 log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path);
-                if (security_getenforce() > 0)
+                if (mac_selinux_enforcing())
                         return -errno;
 
         } 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)
+                        if (mac_selinux_enforcing())
                                 return -errno;
                 } else
                         context_changed = true;
index 639c35b687b786787260184e93d189d7b464f976..159f3f16c24167fbec99a2f151dbcf7024c87bd3 100644 (file)
@@ -8,12 +8,19 @@
 #include "macro.h"
 #include "label.h"
 
+#if HAVE_SELINUX
+#include <selinux/selinux.h>
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(char*, freecon);
+#define _cleanup_freecon_ _cleanup_(freeconp)
+#endif
+
 bool mac_selinux_use(void);
+bool mac_selinux_enforcing(void);
 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_apply(const char *path, const char *label);
index 2d3cf81435bd0e371abe6635e24b361437f49aeb..96924778f55c264e1b7a3c4f33333e1d0a7940fd 100644 (file)
@@ -44,7 +44,7 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
                 char *s;                                                \
                 if (i < 0 || i > max)                                   \
                         return -ERANGE;                                 \
-                if (i < (type) ELEMENTSOF(name##_table)) {              \
+                if (i < (type) ELEMENTSOF(name##_table) && name##_table[i]) { \
                         s = strdup(name##_table[i]);                    \
                         if (!s)                                         \
                                 return -ENOMEM;                         \
index 61809ab06559376f6c29a1893993ee1593ae4302..8f812d7cbe07ebdb31e3dd581796c8144327b68b 100644 (file)
@@ -1054,6 +1054,8 @@ bool string_is_safe(const char *p) {
         if (!p)
                 return false;
 
+        /* Checks if the specified string contains no quotes or control characters */
+
         for (t = p; *t; t++) {
                 if (*t > 0 && *t < ' ') /* no control characters */
                         return false;
index 99bcfde089d7c70cdfda2fac53d6353218c2b1d1..096cb4e5d4d2fa545d99dba10d468d36471e4d9f 100644 (file)
@@ -57,20 +57,15 @@ char *strv_find_startswith(char * const *l, const char *name) {
         return NULL;
 }
 
-void strv_clear(char **l) {
+char **strv_free(char **l) {
         char **k;
 
         if (!l)
-                return;
+                return NULL;
 
         for (k = l; *k; k++)
                 free(*k);
 
-        *l = NULL;
-}
-
-char **strv_free(char **l) {
-        strv_clear(l);
         return mfree(l);
 }
 
@@ -730,19 +725,26 @@ char **strv_sort(char **l) {
         return l;
 }
 
-bool strv_equal(char * const *a, char * const *b) {
+int strv_compare(char * const *a, char * const *b) {
+        int r;
 
-        if (strv_isempty(a))
-                return strv_isempty(b);
+        if (strv_isempty(a)) {
+                if (strv_isempty(b))
+                        return 0;
+                else
+                        return -1;
+        }
 
         if (strv_isempty(b))
-                return false;
+                return 1;
 
-        for ( ; *a || *b; ++a, ++b)
-                if (!streq_ptr(*a, *b))
-                        return false;
+        for ( ; *a || *b; ++a, ++b) {
+                r = strcmp_ptr(*a, *b);
+                if (r != 0)
+                        return r;
+        }
 
-        return true;
+        return 0;
 }
 
 void strv_print(char * const *l) {
@@ -800,12 +802,13 @@ char **strv_shell_escape(char **l, const char *bad) {
         return l;
 }
 
-bool strv_fnmatch(char* const* patterns, const char *s, int flags) {
-        char* const* p;
-
-        STRV_FOREACH(p, patterns)
-                if (fnmatch(*p, s, flags) == 0)
+bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos) {
+        for (size_t i = 0; patterns && patterns[i]; i++)
+                if (fnmatch(patterns[i], s, flags) == 0) {
+                        if (matched_pos)
+                                *matched_pos = i;
                         return true;
+                }
 
         return false;
 }
index f335aeee32fa74a3bf306fbff82fb631a023608a..e7c2b1a604fc36835f7a13353f746a3794aa0043 100644 (file)
@@ -25,8 +25,6 @@ char **strv_free_erase(char **l);
 DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase);
 #define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep)
 
-void strv_clear(char **l);
-
 char **strv_copy(char * const *l);
 size_t strv_length(char * const *l) _pure_;
 
@@ -51,7 +49,10 @@ char **strv_remove(char **l, const char *s);
 char **strv_uniq(char **l);
 bool strv_is_uniq(char * const *l);
 
-bool strv_equal(char * const *a, char * const *b);
+int strv_compare(char * const *a, char * const *b);
+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)))
 
@@ -177,12 +178,15 @@ void strv_print(char * const *l);
 char **strv_reverse(char **l);
 char **strv_shell_escape(char **l, const char *bad);
 
-bool strv_fnmatch(char* const* patterns, const char *s, int flags);
+bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos);
+static inline bool strv_fnmatch(char* const* patterns, const char *s) {
+        return strv_fnmatch_full(patterns, s, 0, NULL);
+}
 
 static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, int flags) {
         assert(s);
         return strv_isempty(patterns) ||
-               strv_fnmatch(patterns, s, flags);
+               strv_fnmatch_full(patterns, s, flags, NULL);
 }
 
 char ***strv_free_free(char ***l);
index 29c9ec9ac45d747ed6e55ad5b9606d9bca5fc3b5..caeba46db43ec191730db3a8a1a9bd339b34a0a8 100644 (file)
@@ -2,10 +2,15 @@
 
 #include <syslog.h>
 
+#include "sd-id128.h"
+
+#include "glob-util.h"
 #include "hexdecoct.h"
 #include "macro.h"
+#include "path-util.h"
 #include "string-table.h"
 #include "syslog-util.h"
+#include "unit-name.h"
 
 int syslog_parse_priority(const char **p, int *priority, bool with_facility) {
         int a = 0, b = 0, c = 0;
@@ -96,3 +101,31 @@ DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_level, int, LOG_DEBUG);
 bool log_level_is_valid(int level) {
         return level >= 0 && level <= LOG_DEBUG;
 }
+
+/* The maximum size for a log namespace length. This is the file name size limit 255 minus the size of a
+ * formatted machine ID minus a separator char */
+#define LOG_NAMESPACE_MAX (NAME_MAX - (SD_ID128_STRING_MAX - 1) - 1)
+
+bool log_namespace_name_valid(const char *s) {
+        /* Let's make sure the namespace fits in a filename that is prefixed with the machine ID and a dot
+         * (so that /var/log/journal/<machine-id>.<namespace> can be created based on it). Also make sure it
+         * is suitable as unit instance name, and does not contain fishy characters. */
+
+        if (!filename_is_valid(s))
+                return false;
+
+        if (strlen(s) > LOG_NAMESPACE_MAX)
+                return false;
+
+        if (!unit_instance_is_valid(s))
+                return false;
+
+        if (!string_is_safe(s))
+                return false;
+
+        /* Let's avoid globbing for now */
+        if (string_is_glob(s))
+                return false;
+
+        return true;
+}
index 8f419e8151a40cad362eb0744109e070c4c8d621..998641fa01dc0f93f2d10f00cfb6078182642322 100644 (file)
@@ -12,3 +12,5 @@ int log_level_from_string(const char *s);
 bool log_level_is_valid(int level);
 
 int syslog_parse_priority(const char **p, int *priority, bool with_facility);
+
+bool log_namespace_name_valid(const char *s);
index 571578c4e4946638217bf17e4b0bc306347796f1..a491f5505e210610825d79f2d6d85d98cdb5a400 100644 (file)
@@ -62,6 +62,29 @@ int parse_uid(const char *s, uid_t *ret) {
         return 0;
 }
 
+int parse_uid_range(const char *s, uid_t *ret_lower, uid_t *ret_upper) {
+        uint32_t u, l;
+        int r;
+
+        assert(s);
+        assert(ret_lower);
+        assert(ret_upper);
+
+        r = parse_range(s, &l, &u);
+        if (r < 0)
+                return r;
+
+        if (l > u)
+                return -EINVAL;
+
+        if (!uid_is_valid(l) || !uid_is_valid(u))
+                return -ENXIO;
+
+        *ret_lower = l;
+        *ret_upper = u;
+        return 0;
+}
+
 char* getlogname_malloc(void) {
         uid_t uid;
         struct stat st;
@@ -496,11 +519,9 @@ int getgroups_alloc(gid_t** gids) {
 
                 free(allocated);
 
-                allocated = new(gid_t, ngroups);
+                p = allocated = new(gid_t, ngroups);
                 if (!allocated)
                         return -ENOMEM;
-
-                p = allocated;
         }
 
         *gids = TAKE_PTR(p);
@@ -945,40 +966,3 @@ int fgetsgent_sane(FILE *stream, struct sgrp **sg) {
         return !!s;
 }
 #endif
-
-int make_salt(char **ret) {
-        static const char table[] =
-                "abcdefghijklmnopqrstuvwxyz"
-                "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-                "0123456789"
-                "./";
-
-        uint8_t raw[16];
-        char *salt, *j;
-        size_t i;
-        int r;
-
-        /* This is a bit like crypt_gensalt_ra(), but doesn't require libcrypt, and doesn't do anything but
-         * SHA512, i.e. is legacy-free and minimizes our deps. */
-
-        assert_cc(sizeof(table) == 64U + 1U);
-
-        /* Insist on the best randomness by setting RANDOM_BLOCK, this is about keeping passwords secret after all. */
-        r = genuine_random_bytes(raw, sizeof(raw), RANDOM_BLOCK);
-        if (r < 0)
-                return r;
-
-        salt = new(char, 3+sizeof(raw)+1+1);
-        if (!salt)
-                return -ENOMEM;
-
-        /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */
-        j = stpcpy(salt, "$6$");
-        for (i = 0; i < sizeof(raw); i++)
-                j[i] = table[raw[i] & 63];
-        j[i++] = '$';
-        j[i] = 0;
-
-        *ret = salt;
-        return 0;
-}
index e11dd9b379c0ef20d70e32543f789fbf2176a46d..6796ac42c1df445a5cfb8bfa5b0d7ceeceede242 100644 (file)
@@ -19,6 +19,7 @@ static inline bool gid_is_valid(gid_t gid) {
 }
 
 int parse_uid(const char *s, uid_t* ret_uid);
+int parse_uid_range(const char *s, uid_t *ret_lower, uid_t *ret_upper);
 
 static inline int parse_gid(const char *s, gid_t *ret_gid) {
         return parse_uid(s, (uid_t*) ret_gid);
@@ -137,6 +138,4 @@ int fgetsgent_sane(FILE *stream, struct sgrp **sg);
 int putsgent_sane(const struct sgrp *sg, FILE *stream);
 #endif
 
-int make_salt(char **ret);
-
 bool is_nologin_shell(const char *shell);
index 12bf77e7013a140930bffff0cdeabbb371fae98b..07831634da784666ed43c9015b433dbb5de87cf7 100644 (file)
@@ -20,6 +20,7 @@
 #include "string-util.h"
 #include "virt.h"
 
+#if defined(__i386__) || defined(__x86_64__)
 static const char *const vm_table[_VIRTUALIZATION_MAX] = {
         [VIRTUALIZATION_XEN]       = "XenVMMXenVMM",
         [VIRTUALIZATION_KVM]       = "KVMKVMKVM",
@@ -36,6 +37,7 @@ static const char *const vm_table[_VIRTUALIZATION_MAX] = {
 };
 
 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(vm, int);
+#endif
 
 static int detect_vm_cpuid(void) {
 
index e1a1a685279a7fddbeaa73b9c06c553f4fe2db74..99938c547a25cd7d846aa4c9707c57fdcb07f378 100644 (file)
@@ -404,10 +404,10 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
                 Print(L"random-seed-mode:       off\n");
                 break;
         case RANDOM_SEED_WITH_SYSTEM_TOKEN:
-                Print(L"random-seed-node:       with-system-token\n");
+                Print(L"random-seed-mode:       with-system-token\n");
                 break;
         case RANDOM_SEED_ALWAYS:
-                Print(L"random-seed-node:       always\n");
+                Print(L"random-seed-mode:       always\n");
                 break;
         default:
                 ;
@@ -1893,8 +1893,8 @@ static VOID config_entry_add_linux(
                 UINTN bufsize = sizeof buf;
                 EFI_FILE_INFO *f;
                 CHAR8 *sections[] = {
-                        (UINT8 *)".osrel",
-                        (UINT8 *)".cmdline",
+                        (CHAR8 *)".osrel",
+                        (CHAR8 *)".cmdline",
                         NULL
                 };
                 UINTN offs[ELEMENTSOF(sections)-1] = {};
index 3edabfedd536f2d199c3fcbdb3f19ab58b8b4b7c..ed81cefcd5d153878ea0bfcdedb9a55bbd54e5c8 100644 (file)
@@ -64,12 +64,19 @@ if conf.get('ENABLE_EFI') == 1 and get_option('gnu-efi') != 'false'
 
         efi_libdir = get_option('efi-libdir')
         if efi_libdir == ''
-                ret = run_command(efi_cc + ['-print-multi-os-directory'])
-                if ret.returncode() == 0
-                        path = join_paths('/usr/lib', ret.stdout().strip())
-                        ret = run_command('realpath', '-e', path)
-                        if ret.returncode() == 0
-                                efi_libdir = ret.stdout().strip()
+                # New location first introduced with gnu-efi 3.0.11
+                efi_libdir = join_paths('/usr/lib/gnuefi', EFI_MACHINE_TYPE_NAME)
+                cmd = run_command('test', '-e', efi_libdir)
+
+                if cmd.returncode() != 0
+                        # Fall back to the old approach
+                        cmd = run_command(efi_cc + ['-print-multi-os-directory'])
+                        if cmd.returncode() == 0
+                                path = join_paths('/usr/lib', cmd.stdout().strip())
+                                cmd = run_command('realpath', '-e', path)
+                                if cmd.returncode() == 0
+                                        efi_libdir = cmd.stdout().strip()
+                                endif
                         endif
                 endif
         endif
@@ -95,20 +102,35 @@ if have_gnu_efi
 
         objcopy = find_program('objcopy')
 
-        efi_ldsdir = get_option('efi-ldsdir')
-        arch_lds = 'elf_@0@_efi.lds'.format(gnu_efi_path_arch)
-        if efi_ldsdir == ''
-                efi_ldsdir = join_paths(efi_libdir, 'gnuefi')
-                cmd = run_command('test', '-f', join_paths(efi_ldsdir, arch_lds))
-                if cmd.returncode() != 0
-                        efi_ldsdir = efi_libdir
-                        cmd = run_command('test', '-f', join_paths(efi_ldsdir, arch_lds))
-                        if cmd.returncode() != 0
-                               error('Cannot find @0@'.format(arch_lds))
+        efi_location_map = [
+                # New locations first introduced with gnu-efi 3.0.11
+                [join_paths(efi_libdir, 'efi.lds'),
+                 join_paths(efi_libdir, 'crt0.o')],
+                # Older locations...
+                [join_paths(efi_libdir, 'gnuefi', 'elf_@0@_efi.lds'.format(gnu_efi_path_arch)),
+                 join_paths(efi_libdir, 'gnuefi', 'crt0-efi-@0@.o'.format(gnu_efi_path_arch))],
+                [join_paths(efi_libdir, 'elf_@0@_efi.lds'.format(gnu_efi_path_arch)),
+                 join_paths(efi_libdir, 'crt0-efi-@0@.o'.format(gnu_efi_path_arch))]]
+        efi_lds = ''
+        foreach location : efi_location_map
+                if efi_lds == ''
+                        cmd = run_command('test', '-f', location[0])
+                        if cmd.returncode() == 0
+                                efi_lds = location[0]
+                                efi_crt0 = location[1]
                         endif
                 endif
+        endforeach
+        if efi_lds == ''
+                if get_option('gnu-efi') == 'true'
+                        error('gnu-efi support requested, but cannot find efi.lds')
+                else
+                        have_gnu_efi = false
+                endif
         endif
+endif
 
+if have_gnu_efi
         compile_args = ['-Wall',
                         '-Wextra',
                         '-std=gnu90',
@@ -133,6 +155,8 @@ if have_gnu_efi
         elif efi_arch == 'ia32'
                 compile_args += ['-mno-sse',
                                  '-mno-mmx']
+        elif efi_arch == 'arm'
+                compile_args += ['-mgeneral-regs-only']
         endif
         if get_option('werror') == true
                 compile_args += ['-Werror']
@@ -145,14 +169,13 @@ if have_gnu_efi
                 compile_args += ['-O2']
         endif
 
-        efi_ldflags = ['-T',
-                       join_paths(efi_ldsdir, arch_lds),
+        efi_ldflags = ['-T', efi_lds,
                        '-shared',
                        '-Bsymbolic',
                        '-nostdlib',
                        '-znocombreloc',
                        '-L', efi_libdir,
-                       join_paths(efi_ldsdir, 'crt0-efi-@0@.o'.format(gnu_efi_path_arch))]
+                       efi_crt0]
         if efi_arch == 'aarch64' or efi_arch == 'arm'
                 # Aarch64 and ARM32 don't have an EFI capable objcopy. Use 'binary'
                 # instead, and add required symbols manually.
@@ -219,11 +242,9 @@ if have_gnu_efi
                 set_variable(tuple[0].underscorify(), so)
                 set_variable(tuple[0].underscorify() + '_stub', stub)
         endforeach
-endif
 
-############################################################
+        ############################################################
 
-if have_gnu_efi
         test_efi_disk_img = custom_target(
                 'test-efi-disk.img',
                 input : [systemd_boot_so, stub_so_stub],
index 6a10cafff020e3b1638e8bc1488d00c9ffca5902..02aab1ec7fc29b8491e62cf6dd15a7949bc18f56 100644 (file)
@@ -22,10 +22,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         UINTN size;
         BOOLEAN secure = FALSE;
         CHAR8 *sections[] = {
-                (UINT8 *)".cmdline",
-                (UINT8 *)".linux",
-                (UINT8 *)".initrd",
-                (UINT8 *)".splash",
+                (CHAR8 *)".cmdline",
+                (CHAR8 *)".linux",
+                (CHAR8 *)".initrd",
+                (CHAR8 *)".splash",
                 NULL
         };
         UINTN addrs[ELEMENTSOF(sections)-1] = {};
index da743dcb9f2ced5e98d48c43126e12f001c54b07..b44f051d95d0019ad267ca872add8dffb0f01cfc 100644 (file)
@@ -189,7 +189,7 @@ static INTN utf8_to_16(CHAR8 *stra, CHAR16 *c) {
         UINTN len;
         UINTN i;
 
-        if (stra[0] < 0x80)
+        if (!(stra[0] & 0x80))
                 len = 1;
         else if ((stra[0] & 0xe0) == 0xc0)
                 len = 2;
index 8518dfde77e0f6f53a1b42adc429de773857a69d..b13f22476c6737893a11e1df21b62abe849f58ba 100644 (file)
@@ -55,7 +55,7 @@ static inline void FileHandleClosep(EFI_FILE_HANDLE *handle) {
         uefi_call_wrapper((*handle)->Close, 1, *handle);
 }
 
-const EFI_GUID loader_guid;
+extern const EFI_GUID loader_guid;
 
 #define UINTN_MAX (~(UINTN)0)
 #define INTN_MAX ((INTN)(UINTN_MAX>>1))
index 659621256b1e67c7204780d2a8087fd358da66cb..3c75be381fc6c9bf2cfb8c20b84df416390cf76b 100644 (file)
@@ -207,14 +207,32 @@ static int list_bus_names(int argc, char **argv, void *userdata) {
         if (r < 0)
                 return log_error_errno(r, "Failed to set empty string: %m");
 
-        r = table_set_sort(table, COLUMN_NAME, (size_t) -1);
+        r = table_set_sort(table, (size_t) COLUMN_NAME, (size_t) -1);
         if (r < 0)
                 return log_error_errno(r, "Failed to set sort column: %m");
 
         if (arg_show_machine)
-                r = table_set_display(table, COLUMN_NAME, COLUMN_PID, COLUMN_PROCESS, COLUMN_USER, COLUMN_CONNECTION, COLUMN_UNIT, COLUMN_SESSION, COLUMN_DESCRIPTION, COLUMN_MACHINE, (size_t) -1);
+                r = table_set_display(table, (size_t) COLUMN_NAME,
+                                             (size_t) COLUMN_PID,
+                                             (size_t) COLUMN_PROCESS,
+                                             (size_t) COLUMN_USER,
+                                             (size_t) COLUMN_CONNECTION,
+                                             (size_t) COLUMN_UNIT,
+                                             (size_t) COLUMN_SESSION,
+                                             (size_t) COLUMN_DESCRIPTION,
+                                             (size_t) COLUMN_MACHINE,
+                                             (size_t) -1);
         else
-                r = table_set_display(table, COLUMN_NAME, COLUMN_PID, COLUMN_PROCESS, COLUMN_USER, COLUMN_CONNECTION, COLUMN_UNIT, COLUMN_SESSION, COLUMN_DESCRIPTION, (size_t) -1);
+                r = table_set_display(table, (size_t) COLUMN_NAME,
+                                             (size_t) COLUMN_PID,
+                                             (size_t) COLUMN_PROCESS,
+                                             (size_t) COLUMN_USER,
+                                             (size_t) COLUMN_CONNECTION,
+                                             (size_t) COLUMN_UNIT,
+                                             (size_t) COLUMN_SESSION,
+                                             (size_t) COLUMN_DESCRIPTION,
+                                             (size_t) -1);
+
         if (r < 0)
                 return log_error_errno(r, "Failed to set columns to display: %m");
 
@@ -1158,7 +1176,7 @@ static int introspect(int argc, char **argv, void *userdata) {
 }
 
 static int message_dump(sd_bus_message *m, FILE *f) {
-        return bus_message_dump(m, f, BUS_MESSAGE_DUMP_WITH_HEADER);
+        return sd_bus_message_dump(m, f, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
 }
 
 static int message_pcap(sd_bus_message *m, FILE *f) {
@@ -2052,7 +2070,7 @@ static int call(int argc, char **argv, void *userdata) {
                 } else if (arg_verbose) {
                         (void) pager_open(arg_pager_flags);
 
-                        r = bus_message_dump(reply, stdout, 0);
+                        r = sd_bus_message_dump(reply, stdout, 0);
                         if (r < 0)
                                 return r;
                 } else {
@@ -2158,7 +2176,7 @@ static int get_property(int argc, char **argv, void *userdata) {
                 } else if (arg_verbose) {
                         (void) pager_open(arg_pager_flags);
 
-                        r = bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY);
+                        r = sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY);
                         if (r < 0)
                                 return r;
                 } else {
index 091b59fe78d12efe50c2a7f5a2a95aea5a3e1c9e..0b3f498bfcb3bb222f5b1139b7255d3f51bf1da8 100644 (file)
@@ -1106,6 +1106,10 @@ const UnitVTable automount_vtable = {
                 "Automount\0"
                 "Install\0",
 
+        .can_transient = true,
+        .can_fail = true,
+        .can_trigger = true,
+
         .init = automount_init,
         .load = automount_load,
         .done = automount_done,
@@ -1132,8 +1136,6 @@ const UnitVTable automount_vtable = {
         .bus_vtable = bus_automount_vtable,
         .bus_set_property = bus_automount_set_property,
 
-        .can_transient = true,
-
         .shutdown = automount_shutdown,
         .supported = automount_supported,
 
index af2efd46fa72190a59a3284bd39e9cbb67297b30..ddd3f408174482f97c17d56261c204f5ada7883e 100644 (file)
@@ -651,34 +651,35 @@ static int lookup_block_device(const char *p, dev_t *ret) {
         r = device_path_parse_major_minor(p, &mode, &rdev);
         if (r == -ENODEV) { /* not a parsable device node, need to go to disk */
                 struct stat st;
+
                 if (stat(p, &st) < 0)
                         return log_warning_errno(errno, "Couldn't stat device '%s': %m", p);
-                rdev = (dev_t)st.st_rdev;
-                dev = (dev_t)st.st_dev;
+
                 mode = st.st_mode;
+                rdev = st.st_rdev;
+                dev = st.st_dev;
         } else if (r < 0)
                 return log_warning_errno(r, "Failed to parse major/minor from path '%s': %m", p);
 
-        if (S_ISCHR(mode)) {
-                log_warning("Device node '%s' is a character device, but block device needed.", p);
-                return -ENOTBLK;
-        } else if (S_ISBLK(mode))
+        if (S_ISCHR(mode))
+                return log_warning_errno(SYNTHETIC_ERRNO(ENOTBLK),
+                                         "Device node '%s' is a character device, but block device needed.", p);
+        if (S_ISBLK(mode))
                 *ret = rdev;
         else if (major(dev) != 0)
                 *ret = dev; /* If this is not a device node then use the block device this file is stored on */
         else {
                 /* If this is btrfs, getting the backing block device is a bit harder */
                 r = btrfs_get_block_device(p, ret);
-                if (r < 0 && r != -ENOTTY)
+                if (r == -ENOTTY)
+                        return log_warning_errno(SYNTHETIC_ERRNO(ENODEV),
+                                                 "'%s' is not a block device node, and file system block device cannot be determined or is not local.", p);
+                if (r < 0)
                         return log_warning_errno(r, "Failed to determine block device backing btrfs file system '%s': %m", p);
-                if (r == -ENOTTY) {
-                        log_warning("'%s' is not a block device node, and file system block device cannot be determined or is not local.", p);
-                        return -ENODEV;
-                }
         }
 
-        /* If this is a LUKS device, try to get the originating block device */
-        (void) block_get_originating(*ret, ret);
+        /* If this is a LUKS/DM device, recursively try to get the originating block device */
+        while (block_get_originating(*ret, ret) > 0);
 
         /* If this is a partition, try to get the originating block device */
         (void) block_get_whole_disk(*ret, ret);
@@ -1526,10 +1527,9 @@ CGroupMask unit_get_members_mask(Unit *u) {
                 Unit *member;
                 Iterator i;
 
-                HASHMAP_FOREACH_KEY(v, member, u->dependencies[UNIT_BEFORE], i) {
+                HASHMAP_FOREACH_KEY(v, member, u->dependencies[UNIT_BEFORE], i)
                         if (UNIT_DEREF(member->slice) == u)
                                 u->cgroup_members_mask |= unit_get_subtree_mask(member); /* note that this calls ourselves again, for the children */
-                }
         }
 
         u->cgroup_members_mask_valid = true;
@@ -2335,7 +2335,15 @@ static void unit_add_siblings_to_cgroup_realize_queue(Unit *u) {
         Unit *slice;
 
         /* This adds the siblings of the specified unit and the siblings of all parent units to the cgroup
-         * queue. (But neither the specified unit itself nor the parents.) */
+         * queue. (But neither the specified unit itself nor the parents.)
+         *
+         * Propagation of realization "side-ways" (i.e. towards siblings) is relevant on cgroup-v1 where
+         * scheduling becomes very weird if two units that own processes reside in the same slice, but one is
+         * realized in the "cpu" hierarchy and one is not (for example because one has CPUWeight= set and the
+         * other does not), because that means individual processes need to be scheduled against whole
+         * cgroups. Let's avoid this asymmetry by always ensuring that units below a slice that are realized
+         * at all are always realized in *all* their hierarchies, and it is sufficient for a unit's sibling
+         * to be realized for the unit itself to be realized too. */
 
         while ((slice = UNIT_DEREF(u->slice))) {
                 Iterator i;
@@ -2343,6 +2351,7 @@ static void unit_add_siblings_to_cgroup_realize_queue(Unit *u) {
                 void *v;
 
                 HASHMAP_FOREACH_KEY(v, m, slice->dependencies[UNIT_BEFORE], i) {
+
                         /* Skip units that have a dependency on the slice but aren't actually in it. */
                         if (UNIT_DEREF(m->slice) != slice)
                                 continue;
@@ -2351,6 +2360,11 @@ static void unit_add_siblings_to_cgroup_realize_queue(Unit *u) {
                         if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(m)))
                                 continue;
 
+                        /* We only enqueue siblings if they were realized once at least, in the main
+                         * hierarchy. */
+                        if (!m->cgroup_realized)
+                                continue;
+
                         /* If the unit doesn't need any new controllers and has current ones realized, it
                          * doesn't need any changes. */
                         if (unit_has_mask_realized(m,
@@ -2648,6 +2662,7 @@ void unit_add_to_cgroup_empty_queue(Unit *u) {
         /* Let's verify that the cgroup is really empty */
         if (!u->cgroup_path)
                 return;
+
         r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
         if (r < 0) {
                 log_unit_debug_errno(u, r, "Failed to determine whether cgroup %s is empty: %m", u->cgroup_path);
@@ -3512,10 +3527,9 @@ void unit_invalidate_cgroup_bpf(Unit *u) {
                 Iterator i;
                 void *v;
 
-                HASHMAP_FOREACH_KEY(v, member, u->dependencies[UNIT_BEFORE], i) {
+                HASHMAP_FOREACH_KEY(v, member, u->dependencies[UNIT_BEFORE], i)
                         if (UNIT_DEREF(member->slice) == u)
                                 unit_invalidate_cgroup_bpf(member);
-                }
         }
 }
 
@@ -3607,8 +3621,8 @@ int unit_get_cpuset(Unit *u, CPUSet *cpus, const char *name) {
                 return r;
         if (r == 0)
                 return -ENODATA;
-        if (r > 0)
-                r = cg_get_attribute("cpuset", u->cgroup_path, name, &v);
+
+        r = cg_get_attribute("cpuset", u->cgroup_path, name, &v);
         if (r == -ENOENT)
                 return -ENODATA;
         if (r < 0)
diff --git a/src/core/core-varlink.c b/src/core/core-varlink.c
new file mode 100644 (file)
index 0000000..8a2984a
--- /dev/null
@@ -0,0 +1,310 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "core-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, 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(uid)),
+                                       JSON_BUILD_PAIR("realName", JSON_BUILD_STRING("Dynamic User")),
+                                       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.DynamicUser")),
+                                       JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("dynamic"))))));
+}
+
+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 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;
+        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.DynamicUser"))
+                return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+        if (uid_is_valid(p.uid))
+                r = dynamic_user_lookup_uid(m, p.uid, &found_name);
+        else if (p.user_name)
+                r = dynamic_user_lookup_name(m, p.user_name, &found_uid);
+        else {
+                Iterator i;
+                DynamicUser *d;
+
+                HASHMAP_FOREACH(d, m->dynamic_users, i) {
+                        r = dynamic_user_current(d, &uid);
+                        if (r == -EAGAIN) /* not realized yet? */
+                                continue;
+                        if (r < 0)
+                                return r;
+
+                        if (!user_match_lookup_parameters(&p, d->name, uid))
+                                continue;
+
+                        if (v) {
+                                r = varlink_notify(link, v);
+                                if (r < 0)
+                                        return r;
+
+                                v = json_variant_unref(v);
+                        }
+
+                        r = build_user_json(d->name, uid, &v);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (!v)
+                        return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+                return varlink_reply(link, v);
+        }
+        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, &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.DynamicUser")),
+                                       JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("dynamic"))))));
+    }
+
+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 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.DynamicUser"))
+                return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+        if (gid_is_valid(p.gid))
+                r = dynamic_user_lookup_uid(m, (uid_t) p.gid, &found_name);
+        else if (p.group_name)
+                r = dynamic_user_lookup_name(m, p.group_name, (uid_t*) &found_gid);
+        else {
+                DynamicUser *d;
+                Iterator i;
+
+                HASHMAP_FOREACH(d, m->dynamic_users, i) {
+                        uid_t uid;
+
+                        r = dynamic_user_current(d, &uid);
+                        if (r == -EAGAIN)
+                                continue;
+                        if (r < 0)
+                                return r;
+
+                        if (!group_match_lookup_parameters(&p, d->name, (gid_t) uid))
+                                continue;
+
+                        if (v) {
+                                r = varlink_notify(link, v);
+                                if (r < 0)
+                                        return r;
+
+                                v = json_variant_unref(v);
+                        }
+
+                        r = build_group_json(d->name, (gid_t) uid, &v);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (!v)
+                        return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+                return varlink_reply(link, v);
+        }
+        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.DynamicUser"))
+                return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+        /* We don't support auxiliary groups with dynamic users. */
+        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;
+
+        if (!MANAGER_IS_SYSTEM(m))
+                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.DynamicUser", 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/core/core-varlink.h b/src/core/core-varlink.h
new file mode 100644 (file)
index 0000000..89818e2
--- /dev/null
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "manager.h"
+
+int manager_varlink_init(Manager *m);
+void manager_varlink_done(Manager *m);
index c6772ba843160968c314d4dbadcba1017f99de23..d8ba3e5d9241e763c3d701f218e0080855ab5950 100644 (file)
@@ -766,6 +766,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
         SD_BUS_PROPERTY("LogRateLimitIntervalUSec", "t", bus_property_get_usec, offsetof(ExecContext, log_ratelimit_interval_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LogRateLimitBurst", "u", bus_property_get_unsigned, offsetof(ExecContext, log_ratelimit_burst), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LogExtraFields", "aay", property_get_log_extra_fields, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("LogNamespace", "s", NULL, offsetof(ExecContext, log_namespace), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("SecureBits", "i", bus_property_get_int, offsetof(ExecContext, secure_bits), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("CapabilityBoundingSet", "t", NULL, offsetof(ExecContext, capability_bounding_set), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("AmbientCapabilities", "t", NULL, offsetof(ExecContext, capability_ambient_set), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1284,6 +1285,9 @@ int bus_exec_context_set_transient_property(
         if (streq(name, "ProtectKernelLogs"))
                 return bus_set_transient_bool(u, name, &c->protect_kernel_logs, message, flags, error);
 
+        if (streq(name, "ProtectClock"))
+                return bus_set_transient_bool(u, name, &c->protect_clock, message, flags, error);
+
         if (streq(name, "ProtectControlGroups"))
                 return bus_set_transient_bool(u, name, &c->protect_control_groups, message, flags, error);
 
@@ -1433,6 +1437,32 @@ int bus_exec_context_set_transient_property(
 
                 return 1;
 
+        } else if (streq(name, "LogNamespace")) {
+                const char *n;
+
+                r = sd_bus_message_read(message, "s", &n);
+                if (r < 0)
+                        return r;
+
+                if (!isempty(n) && !log_namespace_name_valid(n))
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Log namespace name not valid");
+
+                if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+
+                        if (isempty(n)) {
+                                c->log_namespace = mfree(c->log_namespace);
+                                unit_write_settingf(u, flags, name, "%s=", name);
+                        } else {
+                                r = free_and_strdup(&c->log_namespace, n);
+                                if (r < 0)
+                                        return r;
+
+                                unit_write_settingf(u, flags, name, "%s=%s", name, n);
+                        }
+                }
+
+                return 1;
+
         } else if (streq(name, "LogExtraFields")) {
                 size_t n = 0;
 
@@ -1557,6 +1587,7 @@ int bus_exec_context_set_transient_property(
                                         r = seccomp_parse_syscall_filter("@default",
                                                                          -1,
                                                                          c->syscall_filter,
+                                                                         SECCOMP_PARSE_PERMISSIVE |
                                                                          SECCOMP_PARSE_WHITELIST | invert_flag,
                                                                          u->id,
                                                                          NULL, 0);
@@ -1576,7 +1607,9 @@ int bus_exec_context_set_transient_property(
                                 r = seccomp_parse_syscall_filter(n,
                                                                  e,
                                                                  c->syscall_filter,
-                                                                 (c->syscall_whitelist ? SECCOMP_PARSE_WHITELIST : 0) | invert_flag,
+                                                                 SECCOMP_PARSE_LOG | SECCOMP_PARSE_PERMISSIVE |
+                                                                 invert_flag |
+                                                                 (c->syscall_whitelist ? SECCOMP_PARSE_WHITELIST : 0),
                                                                  u->id,
                                                                  NULL, 0);
                                 if (r < 0)
index a7d342257b58f9cb16489c53d1e58b7fd8ce3cd4..6151326f7243e4aa7b631363e752e0fd9a6af0ff 100644 (file)
@@ -3,6 +3,7 @@
 #include "sd-bus.h"
 
 #include "alloc-util.h"
+#include "bus-util.h"
 #include "dbus-job.h"
 #include "dbus-unit.h"
 #include "dbus.h"
index c751e84253e0f557c44545ac2752a14262e34559..60f55aef5fcf53dbf643b092874ab5836d03ac95 100644 (file)
@@ -9,6 +9,7 @@
 #include "architecture.h"
 #include "build.h"
 #include "bus-common-errors.h"
+#include "bus-util.h"
 #include "dbus-cgroup.h"
 #include "dbus-execute.h"
 #include "dbus-job.h"
@@ -1663,7 +1664,7 @@ static int method_lookup_dynamic_user_by_uid(sd_bus_message *message, void *user
         assert(message);
         assert(m);
 
-        assert_cc(sizeof(uid) == sizeof(uint32_t));
+        assert_cc(sizeof(uid_t) == sizeof(uint32_t));
         r = sd_bus_message_read_basic(message, 'u', &uid);
         if (r < 0)
                 return r;
@@ -1712,7 +1713,7 @@ static int method_get_dynamic_users(sd_bus_message *message, void *userdata, sd_
                 if (r == -EAGAIN) /* not realized yet? */
                         continue;
                 if (r < 0)
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Failed to lookup a dynamic user.");
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Failed to look up a dynamic user.");
 
                 r = sd_bus_message_append(reply, "(us)", uid, d->name);
                 if (r < 0)
index 1c5fd2a23b83b2f38379c7b8bd36f7b099278110..496fd5eb6a5da33fcf802321c23d13af42bbfd1b 100644 (file)
@@ -5,6 +5,8 @@
 #include "alloc-util.h"
 #include "bpf-firewall.h"
 #include "bus-common-errors.h"
+#include "bus-polkit.h"
+#include "bus-util.h"
 #include "cgroup-util.h"
 #include "condition.h"
 #include "dbus-job.h"
index a3316c6701831c9ca72a161de8549d690af37b8b..ec8c245fffea9d461bb21ea2453174432385efe7 100644 (file)
@@ -127,7 +127,8 @@ int bus_property_get_triggered_unit(sd_bus *bus, const char *path, const char *i
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {                    \
                         *p = (cast_type) v;                             \
                         unit_write_settingf(u, flags, name,             \
-                                            "%s=%s", name, s);          \
+                                            "%s=%s",                    \
+                                            name, strempty(s));         \
                 }                                                       \
                                                                         \
                 return 1;                                               \
index 3c40f29694401e4525c07b9fd8636f8209eeb6e7..50155f22c6725a9543e463c3bf7182dbb4982930 100644 (file)
@@ -10,6 +10,7 @@
 #include "bus-common-errors.h"
 #include "bus-error.h"
 #include "bus-internal.h"
+#include "bus-polkit.h"
 #include "bus-util.h"
 #include "dbus-automount.h"
 #include "dbus-cgroup.h"
@@ -719,114 +720,6 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void
         return 0;
 }
 
-static int manager_dispatch_sync_bus_names(sd_event_source *es, void *userdata) {
-        _cleanup_strv_free_ char **names = NULL;
-        Manager *m = userdata;
-        const char *name;
-        Iterator i;
-        Unit *u;
-        int r;
-
-        assert(es);
-        assert(m);
-        assert(m->sync_bus_names_event_source == es);
-
-        /* First things first, destroy the defer event so that we aren't triggered again */
-        m->sync_bus_names_event_source = sd_event_source_unref(m->sync_bus_names_event_source);
-
-        /* Let's see if there's anything to do still? */
-        if (!m->api_bus)
-                return 0;
-        if (hashmap_isempty(m->watch_bus))
-                return 0;
-
-        /* OK, let's sync up the names. Let's see which names are currently on the bus. */
-        r = sd_bus_list_names(m->api_bus, &names, NULL);
-        if (r < 0)
-                return log_error_errno(r, "Failed to get initial list of names: %m");
-
-        /* We have to synchronize the current bus names with the
-         * list of active services. To do this, walk the list of
-         * all units with bus names. */
-        HASHMAP_FOREACH_KEY(u, name, m->watch_bus, i) {
-                Service *s = SERVICE(u);
-
-                assert(s);
-
-                if (!streq_ptr(s->bus_name, name)) {
-                        log_unit_warning(u, "Bus name has changed from %s → %s, ignoring.", s->bus_name, name);
-                        continue;
-                }
-
-                /* Check if a service's bus name is in the list of currently
-                 * active names */
-                if (strv_contains(names, name)) {
-                        _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
-                        const char *unique;
-
-                        /* If it is, determine its current owner */
-                        r = sd_bus_get_name_creds(m->api_bus, name, SD_BUS_CREDS_UNIQUE_NAME, &creds);
-                        if (r < 0) {
-                                log_full_errno(r == -ENXIO ? LOG_DEBUG : LOG_ERR, r, "Failed to get bus name owner %s: %m", name);
-                                continue;
-                        }
-
-                        r = sd_bus_creds_get_unique_name(creds, &unique);
-                        if (r < 0) {
-                                log_full_errno(r == -ENXIO ? LOG_DEBUG : LOG_ERR, r, "Failed to get unique name for %s: %m", name);
-                                continue;
-                        }
-
-                        /* Now, let's compare that to the previous bus owner, and
-                         * if it's still the same, all is fine, so just don't
-                         * bother the service. Otherwise, the name has apparently
-                         * changed, so synthesize a name owner changed signal. */
-
-                        if (!streq_ptr(unique, s->bus_name_owner))
-                                UNIT_VTABLE(u)->bus_name_owner_change(u, s->bus_name_owner, unique);
-                } else {
-                        /* So, the name we're watching is not on the bus.
-                         * This either means it simply hasn't appeared yet,
-                         * or it was lost during the daemon reload.
-                         * Check if the service has a stored name owner,
-                         * and synthesize a name loss signal in this case. */
-
-                        if (s->bus_name_owner)
-                                UNIT_VTABLE(u)->bus_name_owner_change(u, s->bus_name_owner, NULL);
-                }
-        }
-
-        return 0;
-}
-
-int manager_enqueue_sync_bus_names(Manager *m) {
-        int r;
-
-        assert(m);
-
-        /* Enqueues a request to synchronize the bus names in a later event loop iteration. The callers generally don't
-         * want us to invoke ->bus_name_owner_change() unit calls from their stack frames as this might result in event
-         * dispatching on its own creating loops, hence we simply create a defer event for the event loop and exit. */
-
-        if (m->sync_bus_names_event_source)
-                return 0;
-
-        r = sd_event_add_defer(m->event, &m->sync_bus_names_event_source, manager_dispatch_sync_bus_names, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to create bus name synchronization event: %m");
-
-        r = sd_event_source_set_priority(m->sync_bus_names_event_source, SD_EVENT_PRIORITY_IDLE);
-        if (r < 0)
-                return log_error_errno(r, "Failed to set event priority: %m");
-
-        r = sd_event_source_set_enabled(m->sync_bus_names_event_source, SD_EVENT_ONESHOT);
-        if (r < 0)
-                return log_error_errno(r, "Failed to set even to oneshot: %m");
-
-        (void) sd_event_source_set_description(m->sync_bus_names_event_source, "manager-sync-bus-names");
-        return 0;
-}
-
 static int bus_setup_api(Manager *m, sd_bus *bus) {
         Iterator i;
         char *name;
@@ -910,10 +803,6 @@ int bus_init_api(Manager *m) {
 
         m->api_bus = TAKE_PTR(bus);
 
-        r = manager_enqueue_sync_bus_names(m);
-        if (r < 0)
-                return r;
-
         return 0;
 }
 
@@ -976,9 +865,10 @@ int bus_init_system(Manager *m) {
 
 int bus_init_private(Manager *m) {
         _cleanup_close_ int fd = -1;
-        union sockaddr_union sa = {};
+        union sockaddr_union sa;
+        socklen_t sa_len;
         sd_event_source *s;
-        int r, salen;
+        int r;
 
         assert(m);
 
@@ -991,7 +881,7 @@ int bus_init_private(Manager *m) {
                 if (getpid_cached() != 1)
                         return 0;
 
-                salen = sockaddr_un_set_path(&sa.un, "/run/systemd/private");
+                r = sockaddr_un_set_path(&sa.un, "/run/systemd/private");
         } else {
                 const char *e, *joined;
 
@@ -1001,10 +891,11 @@ int bus_init_private(Manager *m) {
                                                "XDG_RUNTIME_DIR is not set, refusing.");
 
                 joined = strjoina(e, "/systemd/private");
-                salen = sockaddr_un_set_path(&sa.un, joined);
+                r = sockaddr_un_set_path(&sa.un, joined);
         }
-        if (salen < 0)
-                return log_error_errno(salen, "Can't set path for AF_UNIX socket to bind to: %m");
+        if (r < 0)
+                return log_error_errno(r, "Can't set path for AF_UNIX socket to bind to: %m");
+        sa_len = r;
 
         (void) mkdir_parents_label(sa.un.sun_path, 0755);
         (void) sockaddr_un_unlink(&sa.un);
@@ -1013,7 +904,7 @@ int bus_init_private(Manager *m) {
         if (fd < 0)
                 return log_error_errno(errno, "Failed to allocate private socket: %m");
 
-        r = bind(fd, &sa.sa, salen);
+        r = bind(fd, &sa.sa, sa_len);
         if (r < 0)
                 return log_error_errno(errno, "Failed to bind private socket: %m");
 
@@ -1051,13 +942,10 @@ static void destroy_bus(Manager *m, sd_bus **bus) {
 
         /* Make sure all bus slots watching names are released. */
         HASHMAP_FOREACH(u, m->watch_bus, i) {
-                if (!u->match_bus_slot)
-                        continue;
-
-                if (sd_bus_slot_get_bus(u->match_bus_slot) != *bus)
-                        continue;
-
-                u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
+                if (u->match_bus_slot && sd_bus_slot_get_bus(u->match_bus_slot) == *bus)
+                        u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
+                if (u->get_name_owner_slot && sd_bus_slot_get_bus(u->get_name_owner_slot) == *bus)
+                        u->get_name_owner_slot = sd_bus_slot_unref(u->get_name_owner_slot);
         }
 
         /* Get rid of tracked clients on this bus */
index f1c0fa86c0612440f211fdff5560d169653ef058..d5ba6537eaf5509947f1f4b684e1e08e8ea0a152 100644 (file)
@@ -21,8 +21,6 @@ int bus_fdset_add_all(Manager *m, FDSet *fds);
 void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix);
 int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l);
 
-int manager_enqueue_sync_bus_names(Manager *m);
-
 int bus_foreach_bus(Manager *m, sd_bus_track *subscribed2, int (*send_message)(sd_bus *bus, void *userdata), void *userdata);
 
 int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error);
index 45149e75554c9243b299f93a6ebf8b7d43d2320f..06bbbf8d2c3d1f26f5205d917a49040a718abecd 100644 (file)
@@ -1064,6 +1064,7 @@ const UnitVTable device_vtable = {
                 "Device\0"
                 "Install\0",
 
+        .refuse_after = true,
         .gc_jobs = true,
 
         .init = device_init,
index 75373407b4852cd7cf1739eba93a88c960845231..f1819b36bc2350ebddb2becad0dd7228d2e0ec20 100644 (file)
@@ -529,7 +529,6 @@ int dynamic_user_current(DynamicUser *d, uid_t *ret) {
         int r;
 
         assert(d);
-        assert(ret);
 
         /* Get the currently assigned UID for the user, if there's any. This simply pops the data from the storage socket, and pushes it back in right-away. */
 
@@ -545,7 +544,9 @@ int dynamic_user_current(DynamicUser *d, uid_t *ret) {
         if (r < 0)
                 return r;
 
-        *ret = uid;
+        if (ret)
+                *ret = uid;
+
         return 0;
 }
 
@@ -732,7 +733,6 @@ int dynamic_user_lookup_name(Manager *m, const char *name, uid_t *ret) {
 
         assert(m);
         assert(name);
-        assert(ret);
 
         /* A friendly call for translating a dynamic user's name into its UID */
 
@@ -770,7 +770,7 @@ int dynamic_creds_acquire(DynamicCreds *creds, Manager *m, const char *user, con
 
                 if (creds->user && (!group || streq_ptr(user, group)))
                         creds->group = dynamic_user_ref(creds->user);
-                else {
+                else if (group) {
                         r = dynamic_user_acquire(m, group, &creds->group);
                         if (r < 0) {
                                 if (acquired)
index 5dc111f714d30ec3fd7477782d9a5d57bcb846ea..00a2f2e17e477d0610e680946c81d11f8346ddb9 100644 (file)
@@ -265,15 +265,27 @@ static int open_null_as(int flags, int nfd) {
         return move_fd(fd, nfd, false);
 }
 
-static int connect_journal_socket(int fd, uid_t uid, gid_t gid) {
-        static const union sockaddr_union sa = {
-                .un.sun_family = AF_UNIX,
-                .un.sun_path = "/run/systemd/journal/stdout",
-        };
+static int connect_journal_socket(
+                int fd,
+                const char *log_namespace,
+                uid_t uid,
+                gid_t gid) {
+
+        union sockaddr_union sa;
+        socklen_t sa_len;
         uid_t olduid = UID_INVALID;
         gid_t oldgid = GID_INVALID;
+        const char *j;
         int r;
 
+        j = log_namespace ?
+                strjoina("/run/systemd/journal.", log_namespace, "/stdout") :
+                "/run/systemd/journal/stdout";
+        r = sockaddr_un_set_path(&sa.un, j);
+        if (r < 0)
+                return r;
+        sa_len = r;
+
         if (gid_is_valid(gid)) {
                 oldgid = getgid();
 
@@ -290,7 +302,7 @@ static int connect_journal_socket(int fd, uid_t uid, gid_t gid) {
                 }
         }
 
-        r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0 ? -errno : 0;
+        r = connect(fd, &sa.sa, sa_len) < 0 ? -errno : 0;
 
         /* If we fail to restore the uid or gid, things will likely
            fail later on. This should only happen if an LSM interferes. */
@@ -328,7 +340,7 @@ static int connect_logger_as(
         if (fd < 0)
                 return -errno;
 
-        r = connect_journal_socket(fd, uid, gid);
+        r = connect_journal_socket(fd, context->log_namespace, uid, gid);
         if (r < 0)
                 return r;
 
@@ -371,9 +383,10 @@ static int open_terminal_as(const char *path, int flags, int nfd) {
 }
 
 static int acquire_path(const char *path, int flags, mode_t mode) {
-        union sockaddr_union sa = {};
+        union sockaddr_union sa;
+        socklen_t sa_len;
         _cleanup_close_ int fd = -1;
-        int r, salen;
+        int r;
 
         assert(path);
 
@@ -386,20 +399,19 @@ static int acquire_path(const char *path, int flags, mode_t mode) {
 
         if (errno != ENXIO) /* ENXIO is returned when we try to open() an AF_UNIX file system socket on Linux */
                 return -errno;
-        if (strlen(path) >= sizeof(sa.un.sun_path)) /* Too long, can't be a UNIX socket */
-                return -ENXIO;
 
         /* So, it appears the specified path could be an AF_UNIX socket. Let's see if we can connect to it. */
 
+        r = sockaddr_un_set_path(&sa.un, path);
+        if (r < 0)
+                return r == -EINVAL ? -ENXIO : r;
+        sa_len = r;
+
         fd = socket(AF_UNIX, SOCK_STREAM, 0);
         if (fd < 0)
                 return -errno;
 
-        salen = sockaddr_un_set_path(&sa.un, path);
-        if (salen < 0)
-                return salen;
-
-        if (connect(fd, &sa.sa, salen) < 0)
+        if (connect(fd, &sa.sa, sa_len) < 0)
                 return errno == EINVAL ? -ENXIO : -errno; /* Propagate initial error if we get EINVAL, i.e. we have
                                                            * indication that his wasn't an AF_UNIX socket after all */
 
@@ -408,7 +420,7 @@ static int acquire_path(const char *path, int flags, mode_t mode) {
         else if ((flags & O_ACCMODE) == O_WRONLY)
                 r = shutdown(fd, SHUT_RD);
         else
-                return TAKE_FD(fd);
+                r = 0;
         if (r < 0)
                 return -errno;
 
@@ -1125,7 +1137,7 @@ static int setup_pam(
                 gid_t gid,
                 const char *tty,
                 char ***env,
-                int fds[], size_t n_fds) {
+                const int fds[], size_t n_fds) {
 
 #if HAVE_PAM
 
@@ -1195,7 +1207,7 @@ static int setup_pam(
 
         pam_code = pam_setcred(handle, PAM_ESTABLISH_CRED | flags);
         if (pam_code != PAM_SUCCESS)
-                goto fail;
+                log_debug("pam_setcred() failed, ignoring: %s", pam_strerror(handle, pam_code));
 
         pam_code = pam_open_session(handle, flags);
         if (pam_code != PAM_SUCCESS)
@@ -1402,6 +1414,7 @@ static bool context_has_no_new_privileges(const ExecContext *c) {
                 c->restrict_realtime ||
                 c->restrict_suid_sgid ||
                 exec_context_restrict_namespaces_set(c) ||
+                c->protect_clock ||
                 c->protect_kernel_tunables ||
                 c->protect_kernel_modules ||
                 c->protect_kernel_logs ||
@@ -1564,6 +1577,19 @@ static int apply_protect_kernel_logs(const Unit *u, const ExecContext *c) {
         return seccomp_protect_syslog();
 }
 
+static int apply_protect_clock(const Unit *u, const ExecContext *c)  {
+        assert(u);
+        assert(c);
+
+        if (!c->protect_clock)
+                return 0;
+
+        if (skip_seccomp_unavailable(u, "ProtectClock="))
+                return 0;
+
+        return seccomp_load_syscall_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + SYSCALL_FILTER_SET_CLOCK, SCMP_ACT_ERRNO(EPERM), false);
+}
+
 static int apply_private_devices(const Unit *u, const ExecContext *c) {
         assert(u);
         assert(c);
@@ -1672,7 +1698,7 @@ static int build_environment(
         assert(p);
         assert(ret);
 
-        our_env = new0(char*, 14 + _EXEC_DIRECTORY_TYPE_MAX);
+        our_env = new0(char*, 15 + _EXEC_DIRECTORY_TYPE_MAX);
         if (!our_env)
                 return -ENOMEM;
 
@@ -1781,6 +1807,14 @@ static int build_environment(
                 our_env[n_env++] = x;
         }
 
+        if (c->log_namespace) {
+                x = strjoin("LOG_NAMESPACE=", c->log_namespace);
+                if (!x)
+                        return -ENOMEM;
+
+                our_env[n_env++] = x;
+        }
+
         for (t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
                 _cleanup_free_ char *pre = NULL, *joined = NULL;
                 const char *n;
@@ -1905,6 +1939,9 @@ static bool exec_needs_mount_namespace(
              !strv_isempty(context->directories[EXEC_DIRECTORY_LOGS].paths)))
                 return true;
 
+        if (context->log_namespace)
+                return true;
+
         return false;
 }
 
@@ -2210,7 +2247,7 @@ static int setup_exec_directory(
 
                         if (type != EXEC_DIRECTORY_CONFIGURATION &&
                             readlink_and_make_absolute(p, &target) >= 0) {
-                                _cleanup_free_ char *q = NULL;
+                                _cleanup_free_ char *q = NULL, *q_resolved = NULL, *target_resolved = NULL;
 
                                 /* This already exists and is a symlink? Interesting. Maybe it's one created
                                  * by DynamicUser=1 (see above)?
@@ -2219,13 +2256,22 @@ static int setup_exec_directory(
                                  * since they all support the private/ symlink logic at least in some
                                  * configurations, see above. */
 
+                                r = chase_symlinks(target, NULL, 0, &target_resolved, NULL);
+                                if (r < 0)
+                                        goto fail;
+
                                 q = path_join(params->prefix[type], "private", *rt);
                                 if (!q) {
                                         r = -ENOMEM;
                                         goto fail;
                                 }
 
-                                if (path_equal(q, target)) {
+                                /* /var/lib or friends may be symlinks. So, let's chase them also. */
+                                r = chase_symlinks(q, NULL, CHASE_NONEXISTENT, &q_resolved, NULL);
+                                if (r < 0)
+                                        goto fail;
+
+                                if (path_equal(q_resolved, target_resolved)) {
 
                                         /* Hmm, apparently DynamicUser= was once turned on for this service,
                                          * but is no longer. Let's move the directory back up. */
@@ -2503,6 +2549,9 @@ static bool insist_on_sandboxing(
                 if (!path_equal(bind_mounts[i].source, bind_mounts[i].destination))
                         return true;
 
+        if (context->log_namespace)
+                return true;
+
         return false;
 }
 
@@ -2525,17 +2574,6 @@ static int apply_mount_namespace(
 
         assert(context);
 
-        /* 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. */
-
-        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 (params->flags & EXEC_APPLY_CHROOT) {
                 root_image = context->root_image;
 
@@ -2548,7 +2586,18 @@ static int apply_mount_namespace(
                 return r;
 
         needs_sandboxing = (params->flags & EXEC_APPLY_SANDBOXING) && !(command->flags & EXEC_COMMAND_FULLY_PRIVILEGED);
-        if (needs_sandboxing)
+        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. */
+
+                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");
+                }
+
                 ns_info = (NamespaceInfo) {
                         .ignore_protect_paths = false,
                         .private_dev = context->private_devices,
@@ -2560,7 +2609,7 @@ static int apply_mount_namespace(
                         .mount_apivfs = context->mount_apivfs,
                         .private_mounts = context->private_mounts,
                 };
-        else if (!context->dynamic_user && root_dir)
+        else if (!context->dynamic_user && root_dir)
                 /*
                  * If DynamicUser=no and RootDirectory= is set then lets pass a relaxed
                  * sandbox info, otherwise enforce it, don't ignore protected paths and
@@ -2586,10 +2635,11 @@ static int apply_mount_namespace(
                             context->n_temporary_filesystems,
                             tmp,
                             var,
+                            context->log_namespace,
                             needs_sandboxing ? context->protect_home : PROTECT_HOME_NO,
                             needs_sandboxing ? context->protect_system : PROTECT_SYSTEM_NO,
                             context->mount_flags,
-                            DISSECT_IMAGE_DISCARD_ON_LOOP,
+                            DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK,
                             error_path);
 
         /* If we couldn't set up the namespace this is probably due to a missing capability. setup_namespace() reports
@@ -2803,7 +2853,7 @@ static int close_remaining_fds(
                 int user_lookup_fd,
                 int socket_fd,
                 int exec_fd,
-                int *fds, size_t n_fds) {
+                const int *fds, size_t n_fds) {
 
         size_t n_dont_close = 0;
         int dont_close[n_fds + 12];
@@ -3365,8 +3415,7 @@ static int exec_child(
                                    our_env,
                                    pass_env,
                                    context->environment,
-                                   files_env,
-                                   NULL);
+                                   files_env);
         if (!accum_env) {
                 *exit_status = EXIT_MEMORY;
                 return log_oom();
@@ -3470,13 +3519,17 @@ static int exec_child(
 
                 if (ns_type_supported(NAMESPACE_NET)) {
                         r = setup_netns(runtime->netns_storage_socket);
-                        if (r < 0) {
+                        if (r == -EPERM)
+                                log_unit_warning_errno(unit, r,
+                                                       "PrivateNetwork=yes is configured, but network namespace setup failed, ignoring: %m");
+                        else if (r < 0) {
                                 *exit_status = EXIT_NETWORK;
                                 return log_unit_error_errno(unit, r, "Failed to set up network namespacing: %m");
                         }
                 } else if (context->network_namespace_path) {
                         *exit_status = EXIT_NETWORK;
-                        return log_unit_error_errno(unit, SYNTHETIC_ERRNO(EOPNOTSUPP), "NetworkNamespacePath= is not supported, refusing.");
+                        return log_unit_error_errno(unit, SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                                    "NetworkNamespacePath= is not supported, refusing.");
                 } else
                         log_unit_warning(unit, "PrivateNetwork=yes is configured, but the kernel does not support network namespaces, ignoring.");
         }
@@ -3797,6 +3850,12 @@ static int exec_child(
                         return log_unit_error_errno(unit, r, "Failed to apply kernel log restrictions: %m");
                 }
 
+                r = apply_protect_clock(unit, context);
+                if (r < 0) {
+                        *exit_status = EXIT_SECCOMP;
+                        return log_unit_error_errno(unit, r, "Failed to apply clock restrictions: %m");
+                }
+
                 r = apply_private_devices(unit, context);
                 if (r < 0) {
                         *exit_status = EXIT_SECCOMP;
@@ -4120,6 +4179,8 @@ void exec_context_done(ExecContext *c) {
         c->stdin_data_size = 0;
 
         c->network_namespace_path = mfree(c->network_namespace_path);
+
+        c->log_namespace = mfree(c->log_namespace);
 }
 
 int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
@@ -4437,6 +4498,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
                 "%sProtectKernelTunables: %s\n"
                 "%sProtectKernelModules: %s\n"
                 "%sProtectKernelLogs: %s\n"
+                "%sProtectClock: %s\n"
                 "%sProtectControlGroups: %s\n"
                 "%sPrivateNetwork: %s\n"
                 "%sPrivateUsers: %s\n"
@@ -4458,6 +4520,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
                 prefix, yes_no(c->protect_kernel_tunables),
                 prefix, yes_no(c->protect_kernel_modules),
                 prefix, yes_no(c->protect_kernel_logs),
+                prefix, yes_no(c->protect_clock),
                 prefix, yes_no(c->protect_control_groups),
                 prefix, yes_no(c->private_network),
                 prefix, yes_no(c->private_users),
@@ -4653,6 +4716,9 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
                 }
         }
 
+        if (c->log_namespace)
+                fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace);
+
         if (c->secure_bits) {
                 _cleanup_free_ char *str = NULL;
 
@@ -4823,7 +4889,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
                 r = namespace_flags_to_string(c->restrict_namespaces, &s);
                 if (r >= 0)
                         fprintf(f, "%sRestrictNamespaces: %s\n",
-                                prefix, s);
+                                prefix, strna(s));
         }
 
         if (c->network_namespace_path)
@@ -5305,7 +5371,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;
index c923b1fa21304fd64c8600105497e631c8874835..09c1510aafd3b9c28680036451251740830ceab1 100644 (file)
@@ -250,6 +250,8 @@ struct ExecContext {
 
         int log_level_max;
 
+        char *log_namespace;
+
         bool private_tmp;
         bool private_network;
         bool private_devices;
@@ -258,6 +260,7 @@ struct ExecContext {
         bool protect_kernel_tunables;
         bool protect_kernel_modules;
         bool protect_kernel_logs;
+        bool protect_clock;
         bool protect_control_groups;
         ProtectSystem protect_system;
         ProtectHome protect_home;
index 5982404cf0ceca7f56f44a63578314ba70be0385..9fe30359df36dfa4e367bb318f7d0099c19cf089 100644 (file)
@@ -572,7 +572,7 @@ static void job_print_begin_status_message(Unit *u, JobType t) {
         format = job_get_begin_status_message_format(u, t);
 
         DISABLE_WARNING_FORMAT_NONLITERAL;
-        unit_status_printf(u, "", format);
+        unit_status_printf(u, STATUS_TYPE_NORMAL, "", format);
         REENABLE_WARNING;
 }
 
@@ -797,9 +797,15 @@ _pure_ static const char *job_get_done_status_message_format(Unit *u, JobType t,
         assert(t < _JOB_TYPE_MAX);
 
         if (IN_SET(t, JOB_START, JOB_STOP, JOB_RESTART)) {
+                const UnitStatusMessageFormats *formats = &UNIT_VTABLE(u)->status_message_formats;
+                if (formats->finished_job) {
+                        format = formats->finished_job(u, t, result);
+                        if (format)
+                                return format;
+                }
                 format = t == JOB_START ?
-                        UNIT_VTABLE(u)->status_message_formats.finished_start_job[result] :
-                        UNIT_VTABLE(u)->status_message_formats.finished_stop_job[result];
+                        formats->finished_start_job[result] :
+                        formats->finished_stop_job[result];
                 if (format)
                         return format;
         }
@@ -861,11 +867,10 @@ static void job_print_done_status_message(Unit *u, JobType t, JobResult result)
         else
                 status = job_print_done_status_messages[result].word;
 
-        if (result != JOB_DONE)
-                manager_flip_auto_status(u->manager, true);
-
         DISABLE_WARNING_FORMAT_NONLITERAL;
-        unit_status_printf(u, status, format);
+        unit_status_printf(u,
+                           result == JOB_DONE ? STATUS_TYPE_NORMAL : STATUS_TYPE_NOTICE,
+                           status, format);
         REENABLE_WARNING;
 
         if (t == JOB_START && result == JOB_FAILED) {
index c1f8ac7bb243a367158c1ee534eda8fcb0c79737..69abdb65ba704d018e37e8aa6afc32bf20ac6898 100644 (file)
@@ -116,8 +116,10 @@ $1.PrivateDevices,               config_parse_bool,                  0,
 $1.ProtectKernelTunables,        config_parse_bool,                  0,                             offsetof($1, exec_context.protect_kernel_tunables)
 $1.ProtectKernelModules,         config_parse_bool,                  0,                             offsetof($1, exec_context.protect_kernel_modules)
 $1.ProtectKernelLogs,            config_parse_bool,                  0,                             offsetof($1, exec_context.protect_kernel_logs)
+$1.ProtectClock,                 config_parse_bool,                  0,                             offsetof($1, exec_context.protect_clock)
 $1.ProtectControlGroups,         config_parse_bool,                  0,                             offsetof($1, exec_context.protect_control_groups)
 $1.NetworkNamespacePath,         config_parse_unit_path_printf,      0,                             offsetof($1, exec_context.network_namespace_path)
+$1.LogNamespace,                 config_parse_log_namespace,         0,                             offsetof($1, exec_context)
 $1.PrivateNetwork,               config_parse_bool,                  0,                             offsetof($1, exec_context.private_network)
 $1.PrivateUsers,                 config_parse_bool,                  0,                             offsetof($1, exec_context.private_users)
 $1.PrivateMounts,                config_parse_bool,                  0,                             offsetof($1, exec_context.private_mounts)
index c5ba7b10461231d7895584ba57e2bb21ec1d595b..71a9873da46fbe6404c967b32982c031de02a9cd 100644 (file)
 #include "stat-util.h"
 #include "string-util.h"
 #include "strv.h"
+#include "syslog-util.h"
+#include "time-util.h"
 #include "unit-name.h"
 #include "unit-printf.h"
 #include "user-util.h"
-#include "time-util.h"
 #include "web-util.h"
 
 static int parse_socket_protocol(const char *s) {
@@ -2519,6 +2520,48 @@ int config_parse_log_extra_fields(
         }
 }
 
+int config_parse_log_namespace(
+                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 *k = NULL;
+        ExecContext *c = data;
+        const Unit *u = userdata;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(c);
+
+        if (isempty(rvalue)) {
+                c->log_namespace = mfree(c->log_namespace);
+                return 0;
+        }
+
+        r = unit_full_printf(u, rvalue, &k);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
+                return 0;
+        }
+
+        if (!log_namespace_name_valid(k)) {
+                log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Specified log namespace name is not valid: %s", k);
+                return 0;
+        }
+
+        free_and_replace(c->log_namespace, k);
+        return 0;
+}
+
 int config_parse_unit_condition_path(
                 const char *unit,
                 const char *filename,
@@ -4703,7 +4746,9 @@ int unit_load_fragment(Unit *u) {
                         return r;
 
                 if (null_or_empty(&st)) {
-                        u->load_state = UNIT_MASKED;
+                        /* Unit file is masked */
+
+                        u->load_state = u->perpetual ? UNIT_LOADED : UNIT_MASKED; /* don't allow perpetual units to ever be masked */
                         u->fragment_mtime = 0;
                 } else {
                         u->load_state = UNIT_LOADED;
@@ -4769,7 +4814,7 @@ void unit_dump_config_items(FILE *f) {
                 { config_parse_unsigned,              "UNSIGNED" },
                 { config_parse_iec_size,              "SIZE" },
                 { config_parse_iec_uint64,            "SIZE" },
-                { config_parse_si_size,               "SIZE" },
+                { config_parse_si_uint64,             "SIZE" },
                 { config_parse_bool,                  "BOOLEAN" },
                 { config_parse_string,                "STRING" },
                 { config_parse_path,                  "PATH" },
index 28613ef5b38f8b7e8dfb7f101171cefb8a9475c0..b6b46b2449b118f6cad5876de780871991c1e250 100644 (file)
@@ -107,6 +107,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_keyring_mode);
 CONFIG_PARSER_PROTOTYPE(config_parse_job_timeout_sec);
 CONFIG_PARSER_PROTOTYPE(config_parse_job_running_timeout_sec);
 CONFIG_PARSER_PROTOTYPE(config_parse_log_extra_fields);
+CONFIG_PARSER_PROTOTYPE(config_parse_log_namespace);
 CONFIG_PARSER_PROTOTYPE(config_parse_collect_mode);
 CONFIG_PARSER_PROTOTYPE(config_parse_pid_file);
 CONFIG_PARSER_PROTOTYPE(config_parse_exit_status);
index 23a8ada1ec799b8d024154fdc8103bb48a77be38..ac82c723ea739232d208afc0c244ea57b417f6c9 100644 (file)
@@ -494,7 +494,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
         } else if (streq(key, "quiet") && !value) {
 
                 if (arg_show_status == _SHOW_STATUS_INVALID)
-                        arg_show_status = SHOW_STATUS_AUTO;
+                        arg_show_status = SHOW_STATUS_ERROR;
 
         } else if (streq(key, "debug") && !value) {
 
@@ -711,7 +711,7 @@ static void set_manager_settings(Manager *m) {
         m->kexec_watchdog = arg_kexec_watchdog;
         m->cad_burst_action = arg_cad_burst_action;
 
-        manager_set_show_status(m, arg_show_status);
+        manager_set_show_status(m, arg_show_status, "commandline");
         m->status_unit_format = arg_status_unit_format;
 }
 
@@ -1254,7 +1254,7 @@ static int status_welcome(void) {
         _cleanup_free_ char *pretty_name = NULL, *ansi_color = NULL;
         int r;
 
-        if (IN_SET(arg_show_status, SHOW_STATUS_NO, SHOW_STATUS_AUTO))
+        if (!show_status_on(arg_show_status))
                 return 0;
 
         r = parse_os_release(NULL,
@@ -1747,8 +1747,6 @@ 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);
@@ -1930,7 +1928,7 @@ static int initialize_runtime(
                         status_welcome();
                         hostname_setup();
                         machine_id_setup(NULL, arg_machine_id, NULL);
-                        loopback_setup();
+                        (void) loopback_setup();
                         bump_unix_max_dgram_qlen();
                         bump_file_max_and_nr_open();
                         test_usr();
index 171ff04631e1f033c2c85091eb71ada322f955cd..38f7ba1eb87c0f42a8b98cda44453b58179d78e9 100644 (file)
@@ -29,6 +29,7 @@
 #include "bus-util.h"
 #include "clean-ipc.h"
 #include "clock-util.h"
+#include "core-varlink.h"
 #include "dbus-job.h"
 #include "dbus-manager.h"
 #include "dbus-unit.h"
@@ -44,8 +45,8 @@
 #include "fileio.h"
 #include "fs-util.h"
 #include "hashmap.h"
-#include "io-util.h"
 #include "install.h"
+#include "io-util.h"
 #include "label.h"
 #include "locale-setup.h"
 #include "log.h"
@@ -84,7 +85,8 @@
 #define CGROUPS_AGENT_RCVBUF_SIZE (8*1024*1024)
 
 /* Initial delay and the interval for printing status messages about running jobs */
-#define JOBS_IN_PROGRESS_WAIT_USEC (5*USEC_PER_SEC)
+#define JOBS_IN_PROGRESS_WAIT_USEC (2*USEC_PER_SEC)
+#define JOBS_IN_PROGRESS_QUIET_WAIT_USEC (25*USEC_PER_SEC)
 #define JOBS_IN_PROGRESS_PERIOD_USEC (USEC_PER_SEC / 3)
 #define JOBS_IN_PROGRESS_PERIOD_DIVISOR 3
 
@@ -108,6 +110,12 @@ static int manager_dispatch_timezone_change(sd_event_source *source, const struc
 static int manager_run_environment_generators(Manager *m);
 static int manager_run_generators(Manager *m);
 
+static usec_t manager_watch_jobs_next_time(Manager *m) {
+        return usec_add(now(CLOCK_MONOTONIC),
+                        show_status_on(m->show_status) ? JOBS_IN_PROGRESS_WAIT_USEC :
+                                                         JOBS_IN_PROGRESS_QUIET_WAIT_USEC);
+}
+
 static void manager_watch_jobs_in_progress(Manager *m) {
         usec_t next;
         int r;
@@ -123,7 +131,7 @@ static void manager_watch_jobs_in_progress(Manager *m) {
         if (m->jobs_in_progress_event_source)
                 return;
 
-        next = now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC;
+        next = manager_watch_jobs_next_time(m);
         r = sd_event_add_time(
                         m->event,
                         &m->jobs_in_progress_event_source,
@@ -172,15 +180,15 @@ static void draw_cylon(char buffer[], size_t buflen, unsigned width, unsigned po
         }
 }
 
-void manager_flip_auto_status(Manager *m, bool enable) {
+void manager_flip_auto_status(Manager *m, bool enable, const char *reason) {
         assert(m);
 
         if (enable) {
                 if (m->show_status == SHOW_STATUS_AUTO)
-                        manager_set_show_status(m, SHOW_STATUS_TEMPORARY);
+                        manager_set_show_status(m, SHOW_STATUS_TEMPORARY, reason);
         } else {
                 if (m->show_status == SHOW_STATUS_TEMPORARY)
-                        manager_set_show_status(m, SHOW_STATUS_AUTO);
+                        manager_set_show_status(m, SHOW_STATUS_AUTO, reason);
         }
 }
 
@@ -197,7 +205,7 @@ static void manager_print_jobs_in_progress(Manager *m) {
         assert(m);
         assert(m->n_running_jobs > 0);
 
-        manager_flip_auto_status(m, true);
+        manager_flip_auto_status(m, true, "delay");
 
         print_nr = (m->jobs_in_progress_iteration / JOBS_IN_PROGRESS_PERIOD_DIVISOR) % m->n_running_jobs;
 
@@ -916,8 +924,8 @@ static int manager_setup_notify(Manager *m) {
 
         if (m->notify_fd < 0) {
                 _cleanup_close_ int fd = -1;
-                union sockaddr_union sa = {};
-                int salen;
+                union sockaddr_union sa;
+                socklen_t sa_len;
 
                 /* First free all secondary fields */
                 m->notify_socket = mfree(m->notify_socket);
@@ -933,14 +941,16 @@ static int manager_setup_notify(Manager *m) {
                 if (!m->notify_socket)
                         return log_oom();
 
-                salen = sockaddr_un_set_path(&sa.un, m->notify_socket);
-                if (salen < 0)
-                        return log_error_errno(salen, "Notify socket '%s' not valid for AF_UNIX socket address, refusing.", m->notify_socket);
+                r = sockaddr_un_set_path(&sa.un, m->notify_socket);
+                if (r < 0)
+                        return log_error_errno(r, "Notify socket '%s' not valid for AF_UNIX socket address, refusing.",
+                                               m->notify_socket);
+                sa_len = r;
 
                 (void) mkdir_parents_label(m->notify_socket, 0755);
                 (void) sockaddr_un_unlink(&sa.un);
 
-                r = bind(fd, &sa.sa, salen);
+                r = bind(fd, &sa.sa, sa_len);
                 if (r < 0)
                         return log_error_errno(errno, "bind(%s) failed: %m", m->notify_socket);
 
@@ -1346,6 +1356,7 @@ Manager* manager_free(Manager *m) {
         lookup_paths_flush_generator(&m->lookup_paths);
 
         bus_done(m);
+        manager_varlink_done(m);
 
         exec_runtime_vacuum(m);
         hashmap_free(m->exec_runtime_by_id);
@@ -1373,7 +1384,6 @@ Manager* manager_free(Manager *m) {
         sd_event_source_unref(m->jobs_in_progress_event_source);
         sd_event_source_unref(m->run_queue_event_source);
         sd_event_source_unref(m->user_lookup_event_source);
-        sd_event_source_unref(m->sync_bus_names_event_source);
 
         safe_close(m->signal_fd);
         safe_close(m->notify_fd);
@@ -1610,9 +1620,6 @@ static void manager_ready(Manager *m) {
         manager_recheck_journal(m);
         manager_recheck_dbus(m);
 
-        /* Sync current state of bus names with our set of listening units */
-        (void) manager_enqueue_sync_bus_names(m);
-
         /* Let's finally catch up with any changes that took place while we were reloading/reexecing */
         manager_catchup(m);
 
@@ -1707,6 +1714,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
                         log_warning_errno(r, "Failed to deserialized tracked clients, ignoring: %m");
                 m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
 
+                r = manager_varlink_init(m);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to set up Varlink server, ignoring: %m");
+
                 /* Third, fire things up! */
                 manager_coldplug(m);
 
@@ -2734,11 +2745,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);
+                        manager_set_show_status(m, SHOW_STATUS_YES, "signal");
                         break;
 
                 case 21:
-                        manager_set_show_status(m, SHOW_STATUS_NO);
+                        manager_set_show_status(m, SHOW_STATUS_NO, "signal");
                         break;
 
                 case 22:
@@ -3400,7 +3411,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                         if (s < 0)
                                 log_notice("Failed to parse show-status flag '%s', ignoring.", val);
                         else
-                                manager_set_show_status(m, s);
+                                manager_set_show_status(m, s, "deserialization");
 
                 } else if ((val = startswith(l, "log-level-override="))) {
                         int level;
@@ -3771,12 +3782,12 @@ void manager_check_finished(Manager *m) {
         if (hashmap_size(m->jobs) > 0) {
                 if (m->jobs_in_progress_event_source)
                         /* Ignore any failure, this is only for feedback */
-                        (void) sd_event_source_set_time(m->jobs_in_progress_event_source, now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC);
-
+                        (void) sd_event_source_set_time(m->jobs_in_progress_event_source,
+                                                        manager_watch_jobs_next_time(m));
                 return;
         }
 
-        manager_flip_auto_status(m, false);
+        manager_flip_auto_status(m, false, "boot finished");
 
         /* Notify Type=idle units that we are done now */
         manager_close_idle_pipe(m);
@@ -3815,25 +3826,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,
@@ -3844,13 +3839,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;
 }
@@ -4074,19 +4071,24 @@ void manager_recheck_journal(Manager *m) {
         log_open();
 }
 
-void manager_set_show_status(Manager *m, ShowStatus mode) {
+void manager_set_show_status(Manager *m, ShowStatus mode, const char *reason) {
         assert(m);
-        assert(IN_SET(mode, SHOW_STATUS_AUTO, SHOW_STATUS_NO, SHOW_STATUS_YES, SHOW_STATUS_TEMPORARY));
+        assert(mode >= 0 && mode < _SHOW_STATUS_MAX);
 
         if (!MANAGER_IS_SYSTEM(m))
                 return;
 
-        if (m->show_status != mode)
-                log_debug("%s showing of status.",
-                          mode == SHOW_STATUS_NO ? "Disabling" : "Enabling");
+        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 (IN_SET(mode, SHOW_STATUS_TEMPORARY, SHOW_STATUS_YES))
+        if (enabled)
                 (void) touch("/run/systemd/show-status");
         else
                 (void) unlink("/run/systemd/show-status");
@@ -4108,7 +4110,10 @@ static bool manager_get_show_status(Manager *m, StatusType type) {
         if (type != STATUS_TYPE_EMERGENCY && manager_check_ask_password(m) > 0)
                 return false;
 
-        return IN_SET(m->show_status, SHOW_STATUS_TEMPORARY, SHOW_STATUS_YES);
+        if (type == STATUS_TYPE_NOTICE && m->show_status != SHOW_STATUS_NO)
+                return true;
+
+        return show_status_on(m->show_status);
 }
 
 const char *manager_get_confirm_spawn(Manager *m) {
@@ -4478,7 +4483,7 @@ static void manager_deserialize_uid_refs_one_internal(
 
         r = parse_uid(value, &uid);
         if (r < 0 || uid == 0) {
-                log_debug("Unable to parse UID reference serialization");
+                log_debug("Unable to parse UID reference serialization: " UID_FMT, uid);
                 return;
         }
 
index 51df7f8cd9d067de94fdac7932bba50f05f20147..10c34f95433cfcb0eec1f16c4971dc65b77d6281 100644 (file)
@@ -16,6 +16,7 @@
 #include "list.h"
 #include "prioq.h"
 #include "ratelimit.h"
+#include "varlink.h"
 
 struct libmnt_monitor;
 typedef struct Unit Unit;
@@ -55,6 +56,7 @@ typedef enum ManagerObjective {
 typedef enum StatusType {
         STATUS_TYPE_EPHEMERAL,
         STATUS_TYPE_NORMAL,
+        STATUS_TYPE_NOTICE,
         STATUS_TYPE_EMERGENCY,
 } StatusType;
 
@@ -219,8 +221,6 @@ struct Manager {
         int user_lookup_fds[2];
         sd_event_source *user_lookup_event_source;
 
-        sd_event_source *sync_bus_names_event_source;
-
         UnitFileScope unit_file_scope;
         LookupPaths lookup_paths;
         Hashmap *unit_id_map;
@@ -424,6 +424,8 @@ struct Manager {
         unsigned notifygen;
 
         bool honor_device_enumeration;
+
+        VarlinkServer *varlink_server;
 };
 
 static inline usec_t manager_default_timeout_abort_usec(Manager *m) {
@@ -504,11 +506,11 @@ void disable_printk_ratelimit(void);
 void manager_recheck_dbus(Manager *m);
 void manager_recheck_journal(Manager *m);
 
-void manager_set_show_status(Manager *m, ShowStatus mode);
+void manager_set_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);
+void manager_flip_auto_status(Manager *m, bool enable, const char *reason);
 
 Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path);
 
index e0f08c057e8d84ab3fa484d172d46092c3a15055..3586838f59b3b3a4ed3627399158593327b5bb02 100644 (file)
@@ -22,6 +22,8 @@ libcore_sources = '''
         bpf-firewall.h
         cgroup.c
         cgroup.h
+        core-varlink.c
+        core-varlink.h
         dbus-automount.c
         dbus-automount.h
         dbus-cgroup.c
index 5dfcb6158a434c1317a22cfdacf283ac68dedb59..284e3f6b077236cd382db530a4308dd5939d9551 100644 (file)
@@ -64,7 +64,7 @@ static const MountPoint mount_table[] = {
           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
         { "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",                MS_NOSUID|MS_NOEXEC|MS_STRICTATIME,
           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
         { "securityfs",  "/sys/kernel/security",      "securityfs", NULL,                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
           NULL,          MNT_NONE                   },
index dfed691c43fe34e98e4cc3df4e27bc6010c82210..1c4aefd734f3e01664f419b0f0d7c5e3b41ccec8 100644 (file)
@@ -217,7 +217,7 @@ static void mount_done(Unit *u) {
         m->timer_event_source = sd_event_source_unref(m->timer_event_source);
 }
 
-_pure_ static MountParameters* get_mount_parameters_fragment(Mount *m) {
+static MountParameters* get_mount_parameters_fragment(Mount *m) {
         assert(m);
 
         if (m->from_fragment)
@@ -226,7 +226,7 @@ _pure_ static MountParameters* get_mount_parameters_fragment(Mount *m) {
         return NULL;
 }
 
-_pure_ static MountParameters* get_mount_parameters(Mount *m) {
+static MountParameters* get_mount_parameters(Mount *m) {
         assert(m);
 
         if (m->from_proc_self_mountinfo)
@@ -342,20 +342,18 @@ static int mount_add_device_dependencies(Mount *m) {
         if (!is_device_path(p->what))
                 return 0;
 
-        /* /dev/root is a really weird thing, it's not a real device,
-         * but just a path the kernel exports for the root file system
-         * specified on the kernel command line. Ignore it here. */
-        if (path_equal(p->what, "/dev/root"))
+        /* /dev/root is a really weird thing, it's not a real device, but just a path the kernel exports for
+         * the root file system specified on the kernel command line. Ignore it here. */
+        if (PATH_IN_SET(p->what, "/dev/root", "/dev/nfs"))
                 return 0;
 
         if (path_equal(m->where, "/"))
                 return 0;
 
-        /* Mount units from /proc/self/mountinfo are not bound to devices
-         * by default since they're subject to races when devices are
-         * unplugged. But the user can still force this dep with an
-         * appropriate option (or udev property) so the mount units are
-         * automatically stopped when the device disappears suddenly. */
+        /* Mount units from /proc/self/mountinfo are not bound to devices by default since they're subject to
+         * races when devices are unplugged. But the user can still force this dep with an appropriate option
+         * (or udev property) so the mount units are automatically stopped when the device disappears
+         * suddenly. */
         dep = mount_is_bound_to_device(m) ? UNIT_BINDS_TO : UNIT_REQUIRES;
 
         /* We always use 'what' from /proc/self/mountinfo if mounted */
@@ -365,7 +363,7 @@ static int mount_add_device_dependencies(Mount *m) {
         if (r < 0)
                 return r;
 
-        return 0;
+        return unit_add_blockdev_dependency(UNIT(m), p->what, mask);
 }
 
 static int mount_add_quota_dependencies(Mount *m) {
@@ -537,10 +535,9 @@ static int mount_verify(Mount *m) {
         }
 
         p = get_mount_parameters_fragment(m);
-        if (p && !p->what) {
-                log_unit_error(UNIT(m), "What= setting is missing. Refusing.");
-                return -ENOEXEC;
-        }
+        if (p && !p->what && !UNIT(m)->perpetual)
+                return log_unit_error_errno(UNIT(m), SYNTHETIC_ERRNO(ENOEXEC),
+                                            "What= setting is missing. Refusing.");
 
         if (m->exec_context.pam_name && m->kill_context.kill_mode != KILL_CONTROL_GROUP) {
                 log_unit_error(UNIT(m), "Unit has PAM enabled. Kill mode must be set to control-group'. Refusing.");
@@ -1550,11 +1547,10 @@ static int mount_setup_existing_unit(
                 const char *fstype,
                 MountProcFlags *ret_flags) {
 
-        MountProcFlags flags = MOUNT_PROC_IS_MOUNTED;
         int r;
 
         assert(u);
-        assert(flags);
+        assert(ret_flags);
 
         if (!MOUNT(u)->where) {
                 MOUNT(u)->where = strdup(where);
@@ -1562,13 +1558,27 @@ static int mount_setup_existing_unit(
                         return -ENOMEM;
         }
 
+        /* In case we have multiple mounts established on the same mount point, let's merge flags set already
+         * for the current unit. Note that the flags field is reset on each iteration of reading
+         * /proc/self/mountinfo, hence we know for sure anything already set here is from the current
+         * iteration and thus worthy of taking into account. */
+        MountProcFlags flags =
+                MOUNT(u)->proc_flags | MOUNT_PROC_IS_MOUNTED;
+
         r = update_parameters_proc_self_mountinfo(MOUNT(u), what, options, fstype);
         if (r < 0)
                 return r;
         if (r > 0)
                 flags |= MOUNT_PROC_JUST_CHANGED;
 
-        if (!MOUNT(u)->from_proc_self_mountinfo || FLAGS_SET(MOUNT(u)->proc_flags, MOUNT_PROC_JUST_MOUNTED))
+        /* There are two conditions when we consider a mount point just mounted: when we haven't seen it in
+         * /proc/self/mountinfo before or when MOUNT_MOUNTING is our current state. Why bother with the
+         * latter? Shouldn't that be covered by the former? No, during reload it is not because we might then
+         * encounter a new /proc/self/mountinfo in combination with an old mount unit state (since it stems
+         * from the serialized state), and need to catch up. Since we know that the MOUNT_MOUNTING state is
+         * reached when we wait for the mount to appear we hence can assume that if we are in it, we are
+         * actually seeing it established for the first time. */
+        if (!MOUNT(u)->from_proc_self_mountinfo || MOUNT(u)->state == MOUNT_MOUNTING)
                 flags |= MOUNT_PROC_JUST_MOUNTED;
 
         MOUNT(u)->from_proc_self_mountinfo = true;
@@ -2065,6 +2075,9 @@ const UnitVTable mount_vtable = {
                 "Install\0",
         .private_section = "Mount",
 
+        .can_transient = true,
+        .can_fail = true,
+
         .init = mount_init,
         .load = mount_load,
         .done = mount_done,
@@ -2103,8 +2116,6 @@ const UnitVTable mount_vtable = {
 
         .get_timeout = mount_get_timeout,
 
-        .can_transient = true,
-
         .enumerate_perpetual = mount_enumerate_perpetual,
         .enumerate = mount_enumerate,
         .shutdown = mount_shutdown,
index f121cb7ca412389f9d21535e77bc23669d632337..a461a3cce4340d1076b466856653c76377b46592 100644 (file)
@@ -629,10 +629,9 @@ static int clone_device_node(
         }
 
         /* We're about to fallback to bind-mounting the device
-         * node. So create a dummy bind-mount target. */
-        mac_selinux_create_file_prepare(d, 0);
+         * node. So create a dummy bind-mount target.
+         * Do not prepare device-node SELinux label (see issue 13762) */
         r = mknod(dn, S_IFREG, 0);
-        mac_selinux_create_file_clear();
         if (r < 0 && errno != EEXIST)
                 return log_debug_errno(errno, "mknod() fallback failed for '%s': %m", d);
 
@@ -750,7 +749,7 @@ static int mount_private_dev(MountEntry *m) {
 
         r = dev_setup(temporary_mount, UID_INVALID, GID_INVALID);
         if (r < 0)
-                log_debug_errno(r, "Failed to setup basic device tree at '%s', ignoring: %m", temporary_mount);
+                log_debug_errno(r, "Failed to set up basic device tree at '%s', ignoring: %m", temporary_mount);
 
         /* Create the /dev directory if missing. It is more likely to be
          * missing when the service is started with RootDirectory. This is
@@ -1132,6 +1131,7 @@ static size_t namespace_calculate_mounts(
                 size_t n_temporary_filesystems,
                 const char* tmp_dir,
                 const char* var_tmp_dir,
+                const char* log_namespace,
                 ProtectHome protect_home,
                 ProtectSystem protect_system) {
 
@@ -1166,7 +1166,8 @@ static size_t namespace_calculate_mounts(
                 (ns_info->protect_control_groups ? 1 : 0) +
                 protect_home_cnt + protect_system_cnt +
                 (ns_info->protect_hostname ? 2 : 0) +
-                (namespace_info_mount_apivfs(ns_info) ? ELEMENTSOF(apivfs_table) : 0);
+                (namespace_info_mount_apivfs(ns_info) ? ELEMENTSOF(apivfs_table) : 0) +
+                !!log_namespace;
 }
 
 static void normalize_mounts(const char *root_directory, MountEntry *mounts, size_t *n_mounts) {
@@ -1191,7 +1192,7 @@ static bool root_read_only(
         if (protect_system == PROTECT_SYSTEM_STRICT)
                 return true;
 
-        if (path_strv_contains(read_only_paths, "/"))
+        if (prefixed_path_strv_contains(read_only_paths, "/"))
                 return true;
 
         return false;
@@ -1216,9 +1217,9 @@ static bool home_read_only(
         if (protect_home != PROTECT_HOME_NO)
                 return true;
 
-        if (path_strv_contains(read_only_paths, "/home") ||
-            path_strv_contains(inaccessible_paths, "/home") ||
-            path_strv_contains(empty_directories, "/home"))
+        if (prefixed_path_strv_contains(read_only_paths, "/home") ||
+            prefixed_path_strv_contains(inaccessible_paths, "/home") ||
+            prefixed_path_strv_contains(empty_directories, "/home"))
                 return true;
 
         for (i = 0; i < n_temporary_filesystems; i++)
@@ -1247,6 +1248,7 @@ int setup_namespace(
                 size_t n_temporary_filesystems,
                 const char* tmp_dir,
                 const char* var_tmp_dir,
+                const char *log_namespace,
                 ProtectHome protect_home,
                 ProtectSystem protect_system,
                 unsigned long mount_flags,
@@ -1323,6 +1325,7 @@ int setup_namespace(
                         n_bind_mounts,
                         n_temporary_filesystems,
                         tmp_dir, var_tmp_dir,
+                        log_namespace,
                         protect_home, protect_system);
 
         if (n_mounts > 0) {
@@ -1428,6 +1431,23 @@ int setup_namespace(
                         };
                 }
 
+                if (log_namespace) {
+                        _cleanup_free_ char *q;
+
+                        q = strjoin("/run/systemd/journal.", log_namespace);
+                        if (!q) {
+                                r = -ENOMEM;
+                                goto finish;
+                        }
+
+                        *(m++) = (MountEntry) {
+                                .path_const = "/run/systemd/journal",
+                                .mode = BIND_MOUNT_RECURSIVE,
+                                .read_only = true,
+                                .source_malloc = TAKE_PTR(q),
+                        };
+                }
+
                 assert(mounts + n_mounts == m);
 
                 /* Prepend the root directory where that's necessary */
index 60a6abcd45e2573481a95c33e43ce071a4f6b60d..ef6c9bdc9bdcaa8bb3c68b93ff4e6ee42164d1ef 100644 (file)
@@ -84,6 +84,7 @@ int setup_namespace(
                 size_t n_temporary_filesystems,
                 const char *tmp_dir,
                 const char *var_tmp_dir,
+                const char *log_namespace,
                 ProtectHome protect_home,
                 ProtectSystem protect_system,
                 unsigned long mount_flags,
index ca15ca884e309379d3e1ce3ff1e7ef4cfb1d2b0f..cb75d778afa0f695b9b5667b121f1373589391b1 100644 (file)
@@ -806,6 +806,8 @@ const UnitVTable path_vtable = {
         .private_section = "Path",
 
         .can_transient = true,
+        .can_fail = true,
+        .can_trigger = true,
 
         .init = path_init,
         .done = path_done,
index e8cfedb0d49930eab599ca35907ceae8e7528f3c..76358c416ad1422930faced879b95c2b8523231f 100644 (file)
@@ -619,6 +619,7 @@ const UnitVTable scope_vtable = {
 
         .can_transient = true,
         .can_delegate = true,
+        .can_fail = true,
         .once_only = true,
 
         .init = scope_init,
index 9fd3099feab24f86c28aeaa2bf1fff8ae23453d0..008a8ba9b6aa92786a81ba83c8b286efcff9f6b1 100644 (file)
@@ -143,16 +143,16 @@ 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;
+                const bool enforce = mac_selinux_enforcing();
 
-                enforce = security_getenforce();
-                log_full_errno(enforce != 0 ? LOG_ERR : LOG_WARNING, saved_errno, "Failed to open the SELinux AVC: %m");
+                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
@@ -181,11 +181,11 @@ int mac_selinux_generic_access_check(
                 sd_bus_error *error) {
 
         _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
-        const char *tclass = NULL, *scon = NULL;
-        struct audit_info audit_info = {};
+        const char *tclass, *scon;
         _cleanup_free_ char *cl = NULL;
-        char *fcon = NULL;
+        _cleanup_freecon_ char *fcon = NULL;
         char **cmdline = NULL;
+        const bool enforce = mac_selinux_enforcing();
         int r = 0;
 
         assert(message);
@@ -204,7 +204,7 @@ int mac_selinux_generic_access_check(
                         SD_BUS_CREDS_AUGMENT /* get more bits from /proc */,
                         &creds);
         if (r < 0)
-                goto finish;
+                return r;
 
         /* The SELinux context is something we really should have
          * gotten directly from the message or sender, and not be an
@@ -216,23 +216,37 @@ int mac_selinux_generic_access_check(
 
         r = sd_bus_creds_get_selinux_context(creds, &scon);
         if (r < 0)
-                goto finish;
+                return r;
 
         if (path) {
                 /* Get the file context of the unit file */
 
-                r = getfilecon_raw(path, &fcon);
-                if (r < 0) {
-                        r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
-                        goto finish;
+                if (getfilecon_raw(path, &fcon) < 0) {
+                        r = -errno;
+
+                        log_warning_errno(r, "SELinux getfilecon_raw on '%s' failed%s (perm=%s): %m",
+                                          path,
+                                          enforce ? "" : ", ignoring",
+                                          permission);
+                        if (!enforce)
+                                return 0;
+
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
                 }
 
                 tclass = "service";
+
         } else {
-                r = getcon_raw(&fcon);
-                if (r < 0) {
-                        r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
-                        goto finish;
+                if (getcon_raw(&fcon) < 0) {
+                        r = -errno;
+
+                        log_warning_errno(r, "SELinux getcon_raw failed%s (perm=%s): %m",
+                                          enforce ? "" : ", ignoring",
+                                          permission);
+                        if (!enforce)
+                                return 0;
+
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
                 }
 
                 tclass = "system";
@@ -241,25 +255,23 @@ int mac_selinux_generic_access_check(
         sd_bus_creds_get_cmdline(creds, &cmdline);
         cl = strv_join(cmdline, " ");
 
-        audit_info.creds = creds;
-        audit_info.path = path;
-        audit_info.cmdline = cl;
+        struct audit_info audit_info = {
+                .creds = creds,
+                .path = path,
+                .cmdline = cl,
+        };
 
         r = selinux_check_access(scon, fcon, tclass, permission, &audit_info);
-        if (r < 0)
-                r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
-
-        log_debug("SELinux access check scon=%s tcon=%s tclass=%s perm=%s path=%s cmdline=%s: %i", scon, fcon, tclass, permission, path, cl, r);
-
-finish:
-        freecon(fcon);
+        if (r < 0) {
+                r = errno_or_else(EPERM);
 
-        if (r < 0 && security_getenforce() != 1) {
-                sd_bus_error_free(error);
-                r = 0;
+                if (enforce)
+                        sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
         }
 
-        return r;
+        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);
+        return enforce ? r : 0;
 }
 
 #else
index 1e75930f5719b5c3200e14f3c0bcab9a27e66326..da2e6cbd740695c401df9e8e1270a0be3648da31 100644 (file)
@@ -3,7 +3,6 @@
 
 #include "sd-bus.h"
 
-#include "bus-util.h"
 #include "manager.h"
 
 int mac_selinux_generic_access_check(sd_bus_message *message, const char *path, const char *permission, sd_bus_error *error);
index 49ad166c26045643c9fc4b24505a929f40a0bd4d..9a26271f7241c4f58880c0b53a6a912be3b4e346 100644 (file)
@@ -650,24 +650,37 @@ static int service_add_default_dependencies(Service *s) {
         return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
 }
 
-static void service_fix_output(Service *s) {
+static void service_fix_stdio(Service *s) {
         assert(s);
 
-        /* If nothing has been explicitly configured, patch default output in. If input is socket/tty we avoid this
-         * however, since in that case we want output to default to the same place as we read input from. */
+        /* 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. */
+
+        if (s->exec_context.std_input == EXEC_INPUT_NULL &&
+            s->exec_context.stdin_data_size > 0)
+                s->exec_context.std_input = EXEC_INPUT_DATA;
+
+        if (IN_SET(s->exec_context.std_input,
+                    EXEC_INPUT_TTY,
+                    EXEC_INPUT_TTY_FORCE,
+                    EXEC_INPUT_TTY_FAIL,
+                    EXEC_INPUT_SOCKET,
+                    EXEC_INPUT_NAMED_FD))
+                return;
+
+        /* We assume these listed inputs refer to bidirectional streams, and hence duplicating them from
+         * stdin to stdout/stderr makes sense and hence leaving EXEC_OUTPUT_INHERIT in place makes sense,
+         * too. Outputs such as regular files or sealed data memfds otoh don't really make sense to be
+         * duplicated for both input and output at the same time (since they then would cause a feedback
+         * loop), hence override EXEC_OUTPUT_INHERIT with the default stderr/stdout setting.  */
 
         if (s->exec_context.std_error == EXEC_OUTPUT_INHERIT &&
-            s->exec_context.std_output == EXEC_OUTPUT_INHERIT &&
-            s->exec_context.std_input == EXEC_INPUT_NULL)
+            s->exec_context.std_output == EXEC_OUTPUT_INHERIT)
                 s->exec_context.std_error = UNIT(s)->manager->default_std_error;
 
-        if (s->exec_context.std_output == EXEC_OUTPUT_INHERIT &&
-            s->exec_context.std_input == EXEC_INPUT_NULL)
+        if (s->exec_context.std_output == EXEC_OUTPUT_INHERIT)
                 s->exec_context.std_output = UNIT(s)->manager->default_std_output;
-
-        if (s->exec_context.std_input == EXEC_INPUT_NULL &&
-            s->exec_context.stdin_data_size > 0)
-                s->exec_context.std_input = EXEC_INPUT_DATA;
 }
 
 static int service_setup_bus_name(Service *s) {
@@ -715,7 +728,7 @@ static int service_add_extras(Service *s) {
         if (s->type == SERVICE_ONESHOT && !s->start_timeout_defined)
                 s->timeout_start_usec = USEC_INFINITY;
 
-        service_fix_output(s);
+        service_fix_stdio(s);
 
         r = unit_patch_contexts(UNIT(s));
         if (r < 0)
@@ -3488,6 +3501,12 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                 break;
 
                         case SERVICE_STOP_POST:
+
+                                if (control_pid_good(s) <= 0)
+                                        service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
+
+                                break;
+
                         case SERVICE_FINAL_SIGTERM:
                         case SERVICE_FINAL_SIGKILL:
 
@@ -3637,6 +3656,10 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                 break;
 
                         case SERVICE_STOP_POST:
+                                if (main_pid_good(s) <= 0)
+                                        service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
+                                break;
+
                         case SERVICE_FINAL_SIGTERM:
                         case SERVICE_FINAL_SIGKILL:
                                 if (main_pid_good(s) <= 0)
@@ -4062,24 +4085,17 @@ static int service_get_timeout(Unit *u, usec_t *timeout) {
         return 1;
 }
 
-static void service_bus_name_owner_change(
-                Unit *u,
-                const char *old_owner,
-                const char *new_owner) {
+static void service_bus_name_owner_change(Unit *u, const char *new_owner) {
 
         Service *s = SERVICE(u);
         int r;
 
         assert(s);
 
-        assert(old_owner || new_owner);
-
-        if (old_owner && new_owner)
-                log_unit_debug(u, "D-Bus name %s changed owner from %s to %s", s->bus_name, old_owner, new_owner);
-        else if (old_owner)
-                log_unit_debug(u, "D-Bus name %s no longer registered by %s", s->bus_name, old_owner);
+        if (new_owner)
+                log_unit_debug(u, "D-Bus name %s now owned by %s", s->bus_name, new_owner);
         else
-                log_unit_debug(u, "D-Bus name %s now registered by %s", s->bus_name, new_owner);
+                log_unit_debug(u, "D-Bus name %s now not owned by anyone.", s->bus_name);
 
         s->bus_name_good = !!new_owner;
 
@@ -4302,6 +4318,18 @@ static int service_can_clean(Unit *u, ExecCleanMask *ret) {
         return exec_context_get_clean_mask(&s->exec_context, ret);
 }
 
+static const char *service_finished_job(Unit *u, JobType t, JobResult result) {
+        if (t == JOB_START && result == JOB_DONE) {
+                Service *s = SERVICE(u);
+
+                if (s->type == SERVICE_ONESHOT)
+                        return "Finished %s.";
+        }
+
+        /* Fall back to generic */
+        return NULL;
+}
+
 static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
         [SERVICE_RESTART_NO] = "no",
         [SERVICE_RESTART_ON_SUCCESS] = "on-success",
@@ -4391,6 +4419,7 @@ const UnitVTable service_vtable = {
 
         .can_transient = true,
         .can_delegate = true,
+        .can_fail = true,
 
         .init = service_init,
         .done = service_done,
@@ -4448,7 +4477,6 @@ const UnitVTable service_vtable = {
                         [1] = "Stopping %s...",
                 },
                 .finished_start_job = {
-                        [JOB_DONE]       = "Started %s.",
                         [JOB_FAILED]     = "Failed to start %s.",
                         [JOB_SKIPPED]    = "Skipped %s.",
                 },
@@ -4456,5 +4484,6 @@ const UnitVTable service_vtable = {
                         [JOB_DONE]       = "Stopped %s.",
                         [JOB_FAILED]     = "Stopped (with error) %s.",
                 },
+                .finished_job = service_finished_job,
         },
 };
index c998b51abdb74e68c1580de2a02b1d99c30a2169..9d7358a9c12d741d7283be51a88be4124a9716e9 100644 (file)
@@ -16,6 +16,7 @@
 
 static const char* const show_status_table[_SHOW_STATUS_MAX] = {
         [SHOW_STATUS_NO]        = "no",
+        [SHOW_STATUS_ERROR]     = "error",
         [SHOW_STATUS_AUTO]      = "auto",
         [SHOW_STATUS_TEMPORARY] = "temporary",
         [SHOW_STATUS_YES]       = "yes",
index 247caec77c4290fd7a3493b0341a33929234210f..178f624d6ce0f3ddd4abf1d7cfc8e5cf14da4c3d 100644 (file)
@@ -8,10 +8,11 @@
 /* Manager status */
 
 typedef enum ShowStatus {
-        SHOW_STATUS_NO,
-        SHOW_STATUS_AUTO,
-        SHOW_STATUS_TEMPORARY,
-        SHOW_STATUS_YES,
+        SHOW_STATUS_NO,         /* printing of status is disabled */
+        SHOW_STATUS_ERROR,      /* only print errors */
+        SHOW_STATUS_AUTO,       /* disabled but may flip to _TEMPORARY */
+        SHOW_STATUS_TEMPORARY,  /* enabled temporarily, may flip back to _AUTO */
+        SHOW_STATUS_YES,        /* printing of status is enabled */
         _SHOW_STATUS_MAX,
         _SHOW_STATUS_INVALID = -1,
 } ShowStatus;
@@ -28,6 +29,9 @@ typedef enum StatusUnitFormat {
         _STATUS_UNIT_FORMAT_INVALID = -1,
 } StatusUnitFormat;
 
+static inline bool show_status_on(ShowStatus s) {
+        return IN_SET(s, SHOW_STATUS_TEMPORARY, SHOW_STATUS_YES);
+}
 ShowStatus show_status_from_string(const char *v) _const_;
 const char* show_status_to_string(ShowStatus s) _pure_;
 int parse_show_status(const char *v, ShowStatus *ret);
index 633be68337014e033bb49ca183050812d5a1a6de..4a0e3a253e854d23c71b00d128ba66ad5ac8b44b 100644 (file)
@@ -3423,6 +3423,8 @@ const UnitVTable socket_vtable = {
         .private_section = "Socket",
 
         .can_transient = true,
+        .can_trigger = true,
+        .can_fail = true,
 
         .init = socket_init,
         .done = socket_done,
index 225488282e889c8b4b4131605a7ef693b41e7b4f..d4da840c0f4ef1506d3bc0d5eed352d433abc675 100644 (file)
@@ -184,21 +184,45 @@ static int swap_arm_timer(Swap *s, usec_t usec) {
         return 0;
 }
 
+static SwapParameters* swap_get_parameters(Swap *s) {
+        assert(s);
+
+        if (s->from_proc_swaps)
+                return &s->parameters_proc_swaps;
+
+        if (s->from_fragment)
+                return &s->parameters_fragment;
+
+        return NULL;
+}
+
 static int swap_add_device_dependencies(Swap *s) {
+        UnitDependencyMask mask;
+        SwapParameters *p;
+        int r;
+
         assert(s);
 
         if (!s->what)
                 return 0;
 
-        if (!s->from_fragment)
+        p = swap_get_parameters(s);
+        if (!p)
                 return 0;
 
-        if (is_device_path(s->what))
-                return unit_add_node_dependency(UNIT(s), s->what, UNIT_BINDS_TO, UNIT_DEPENDENCY_FILE);
+        mask = s->from_proc_swaps ? UNIT_DEPENDENCY_PROC_SWAP : UNIT_DEPENDENCY_FILE;
 
-        /* File based swap devices need to be ordered after systemd-remount-fs.service,
-         * since they might need a writable file system. */
-        return unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_REMOUNT_FS_SERVICE, true, UNIT_DEPENDENCY_FILE);
+        if (is_device_path(p->what)) {
+                r = unit_add_node_dependency(UNIT(s), p->what, UNIT_REQUIRES, mask);
+                if (r < 0)
+                        return r;
+
+                return unit_add_blockdev_dependency(UNIT(s), p->what, mask);
+        }
+
+        /* File based swap devices need to be ordered after systemd-remount-fs.service, since they might need
+         * a writable file system. */
+        return unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_REMOUNT_FS_SERVICE, true, mask);
 }
 
 static int swap_add_default_dependencies(Swap *s) {
@@ -700,9 +724,15 @@ static void swap_enter_active(Swap *s, SwapResult f) {
 static void swap_enter_dead_or_active(Swap *s, SwapResult f) {
         assert(s);
 
-        if (s->from_proc_swaps)
+        if (s->from_proc_swaps) {
+                Swap *other;
+
                 swap_enter_active(s, f);
-        else
+
+                LIST_FOREACH_OTHERS(same_devnode, other, s)
+                        if (UNIT(other)->job)
+                                swap_enter_dead_or_active(other, f);
+        } else
                 swap_enter_dead(s, f);
 }
 
@@ -1590,6 +1620,8 @@ const UnitVTable swap_vtable = {
                 "Install\0",
         .private_section = "Swap",
 
+        .can_fail = true,
+
         .init = swap_init,
         .load = swap_load,
         .done = swap_done,
index 5d1ddd7620c9666e751bbb0b11360d602a3095b9..8331832c7a208dcfe7d380cd30599682431da8f8 100644 (file)
@@ -21,6 +21,8 @@ systemdsystemunitpath=${systemdsystemconfdir}:/etc/systemd/system:/run/systemd/s
 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
index 051ca76273223a97e0539b5596b57230b2f10fe0..57d979d52da1e58d3e7b79319cae29418ee9cf66 100644 (file)
@@ -895,6 +895,10 @@ const UnitVTable timer_vtable = {
                 "Install\0",
         .private_section = "Timer",
 
+        .can_transient = true,
+        .can_fail = true,
+        .can_trigger = true,
+
         .init = timer_init,
         .done = timer_done,
         .load = timer_load,
@@ -923,6 +927,4 @@ const UnitVTable timer_vtable = {
 
         .bus_vtable = bus_timer_vtable,
         .bus_set_property = bus_timer_set_property,
-
-        .can_transient = true,
 };
index 8d67f9ce1abe285374b9bd7d80bfd9573976af87..49f43e03278c39d4e31a2b07616262330af1ca93 100644 (file)
@@ -425,7 +425,9 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
                         else
                                 status = " SKIP ";
 
-                        unit_status_printf(delete->unit, status,
+                        unit_status_printf(delete->unit,
+                                           STATUS_TYPE_NOTICE,
+                                           status,
                                            "Ordering cycle found, skipping %s");
                         transaction_delete_unit(tr, delete->unit);
                         return -EAGAIN;
index 399a8cf655b1c8b7c68cdad1dbaaec2c39a5daa1..2816bcef5504e780a3d8f2836cdfbdb917ea816c 100644 (file)
@@ -1059,13 +1059,33 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
             !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_SYSLOG, EXEC_OUTPUT_SYSLOG_AND_CONSOLE) &&
+            !c->log_namespace)
                 return 0;
 
-        /* If syslog or kernel logging is requested, make sure our own
-         * logging daemon is run first. */
+        /* If syslog or kernel logging is requested (or log namespacing is), make sure our own logging daemon
+         * is run first. */
+
+        if (c->log_namespace) {
+                _cleanup_free_ char *socket_unit = NULL, *varlink_socket_unit = NULL;
+
+                r = unit_name_build_from_type("systemd-journald", c->log_namespace, UNIT_SOCKET, &socket_unit);
+                if (r < 0)
+                        return r;
+
+                r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, socket_unit, true, UNIT_DEPENDENCY_FILE);
+                if (r < 0)
+                        return r;
+
+                r = unit_name_build_from_type("systemd-journald-varlink", c->log_namespace, UNIT_SOCKET, &varlink_socket_unit);
+                if (r < 0)
+                        return r;
 
-        r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, true, UNIT_DEPENDENCY_FILE);
+                r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, varlink_socket_unit, true, UNIT_DEPENDENCY_FILE);
+                if (r < 0)
+                        return r;
+        } else
+                r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, true, UNIT_DEPENDENCY_FILE);
         if (r < 0)
                 return r;
 
@@ -1639,7 +1659,7 @@ static bool unit_test_assert(Unit *u) {
         return u->assert_result;
 }
 
-void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) {
+void unit_status_printf(Unit *u, StatusType status_type, const char *status, const char *unit_status_msg_format) {
         const char *d;
 
         d = unit_status_string(u);
@@ -1647,7 +1667,7 @@ void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg
                 d = strjoina(ANSI_HIGHLIGHT, d, ANSI_NORMAL);
 
         DISABLE_WARNING_FORMAT_NONLITERAL;
-        manager_status_printf(u->manager, STATUS_TYPE_NORMAL, status, unit_status_msg_format, d);
+        manager_status_printf(u->manager, status_type, status, unit_status_msg_format, d);
         REENABLE_WARNING;
 }
 
@@ -2937,12 +2957,28 @@ int unit_add_dependency(
                 return 0;
         }
 
-        if ((d == UNIT_BEFORE && other->type == UNIT_DEVICE) ||
-            (d == UNIT_AFTER && u->type == UNIT_DEVICE)) {
-                log_unit_warning(u, "Dependency Before=%s ignored (.device units cannot be delayed)", other->id);
+        if (d == UNIT_AFTER && UNIT_VTABLE(u)->refuse_after) {
+                log_unit_warning(u, "Requested dependency After=%s ignored (%s units cannot be delayed).", other->id, unit_type_to_string(u->type));
+                return 0;
+        }
+
+        if (d == UNIT_BEFORE && UNIT_VTABLE(other)->refuse_after) {
+                log_unit_warning(u, "Requested dependency Before=%s ignored (%s units cannot be delayed).", other->id, unit_type_to_string(other->type));
                 return 0;
         }
 
+        if (d == UNIT_ON_FAILURE && !UNIT_VTABLE(u)->can_fail) {
+                log_unit_warning(u, "Requested dependency OnFailure=%s ignored (%s units cannot fail).", other->id, unit_type_to_string(u->type));
+                return 0;
+        }
+
+        if (d == UNIT_TRIGGERS && !UNIT_VTABLE(u)->can_trigger)
+                return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL),
+                                            "Requested dependency Triggers=%s refused (%s units cannot trigger other units).", other->id, unit_type_to_string(u->type));
+        if (d == UNIT_TRIGGERED_BY && !UNIT_VTABLE(other)->can_trigger)
+                return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL),
+                                            "Requested dependency TriggeredBy=%s refused (%s units cannot trigger other units).", other->id, unit_type_to_string(other->type));
+
         r = unit_add_dependency_hashmap(u->dependencies + d, other, mask, 0);
         if (r < 0)
                 return r;
@@ -3185,24 +3221,21 @@ int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
 }
 
 static int signal_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
-        const char *name, *old_owner, *new_owner;
+        const char *new_owner;
         Unit *u = userdata;
         int r;
 
         assert(message);
         assert(u);
 
-        r = sd_bus_message_read(message, "sss", &name, &old_owner, &new_owner);
+        r = sd_bus_message_read(message, "sss", NULL, NULL, &new_owner);
         if (r < 0) {
                 bus_log_parse_error(r);
                 return 0;
         }
 
-        old_owner = empty_to_null(old_owner);
-        new_owner = empty_to_null(new_owner);
-
         if (UNIT_VTABLE(u)->bus_name_owner_change)
-                UNIT_VTABLE(u)->bus_name_owner_change(u, old_owner, new_owner);
+                UNIT_VTABLE(u)->bus_name_owner_change(u, empty_to_null(new_owner));
 
         return 0;
 }
@@ -3218,42 +3251,35 @@ static int get_name_owner_handler(sd_bus_message *message, void *userdata, sd_bu
 
         u->get_name_owner_slot = sd_bus_slot_unref(u->get_name_owner_slot);
 
-        if (sd_bus_error_is_set(error)) {
-                log_error("Failed to get name owner from bus: %s", error->message);
-                return 0;
-        }
-
         e = sd_bus_message_get_error(message);
-        if (sd_bus_error_has_name(e, "org.freedesktop.DBus.Error.NameHasNoOwner"))
-                return 0;
-
         if (e) {
-                log_error("Unexpected error response from GetNameOwner: %s", e->message);
-                return 0;
-        }
+                if (!sd_bus_error_has_name(e, "org.freedesktop.DBus.Error.NameHasNoOwner"))
+                        log_unit_error(u, "Unexpected error response from GetNameOwner(): %s", e->message);
 
-        r = sd_bus_message_read(message, "s", &new_owner);
-        if (r < 0) {
-                bus_log_parse_error(r);
-                return 0;
-        }
+                new_owner = NULL;
+        } else {
+                r = sd_bus_message_read(message, "s", &new_owner);
+                if (r < 0)
+                        return bus_log_parse_error(r);
 
-        new_owner = empty_to_null(new_owner);
+                assert(!isempty(new_owner));
+        }
 
         if (UNIT_VTABLE(u)->bus_name_owner_change)
-                UNIT_VTABLE(u)->bus_name_owner_change(u, NULL, new_owner);
+                UNIT_VTABLE(u)->bus_name_owner_change(u, new_owner);
 
         return 0;
 }
 
 int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name) {
         const char *match;
+        int r;
 
         assert(u);
         assert(bus);
         assert(name);
 
-        if (u->match_bus_slot)
+        if (u->match_bus_slot || u->get_name_owner_slot)
                 return -EBUSY;
 
         match = strjoina("type='signal',"
@@ -3263,19 +3289,27 @@ int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name) {
                          "member='NameOwnerChanged',"
                          "arg0='", name, "'");
 
-        int r = sd_bus_add_match_async(bus, &u->match_bus_slot, match, signal_name_owner_changed, NULL, u);
+        r = sd_bus_add_match_async(bus, &u->match_bus_slot, match, signal_name_owner_changed, NULL, u);
         if (r < 0)
                 return r;
 
-        return sd_bus_call_method_async(bus,
-                                        &u->get_name_owner_slot,
-                                        "org.freedesktop.DBus",
-                                        "/org/freedesktop/DBus",
-                                        "org.freedesktop.DBus",
-                                        "GetNameOwner",
-                                        get_name_owner_handler,
-                                        u,
-                                        "s", name);
+        r = sd_bus_call_method_async(
+                        bus,
+                        &u->get_name_owner_slot,
+                        "org.freedesktop.DBus",
+                        "/org/freedesktop/DBus",
+                        "org.freedesktop.DBus",
+                        "GetNameOwner",
+                        get_name_owner_handler,
+                        u,
+                        "s", name);
+        if (r < 0) {
+                u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
+                return r;
+        }
+
+        log_unit_debug(u, "Watching D-Bus name '%s'.", name);
+        return 0;
 }
 
 int unit_watch_bus_name(Unit *u, const char *name) {
@@ -3298,6 +3332,7 @@ int unit_watch_bus_name(Unit *u, const char *name) {
         r = hashmap_put(u->manager->watch_bus, name, u);
         if (r < 0) {
                 u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
+                u->get_name_owner_slot = sd_bus_slot_unref(u->get_name_owner_slot);
                 return log_warning_errno(r, "Failed to put bus name to hashmap: %m");
         }
 
@@ -3831,8 +3866,8 @@ int unit_deserialize_skip(FILE *f) {
 }
 
 int unit_add_node_dependency(Unit *u, const char *what, UnitDependency dep, UnitDependencyMask mask) {
-        Unit *device;
         _cleanup_free_ char *e = NULL;
+        Unit *device;
         int r;
 
         assert(u);
@@ -3844,8 +3879,7 @@ int unit_add_node_dependency(Unit *u, const char *what, UnitDependency dep, Unit
         if (!is_device_path(what))
                 return 0;
 
-        /* When device units aren't supported (such as in a
-         * container), don't create dependencies on them. */
+        /* When device units aren't supported (such as in a container), don't create dependencies on them. */
         if (!unit_type_supported(UNIT_DEVICE))
                 return 0;
 
@@ -3865,6 +3899,33 @@ int unit_add_node_dependency(Unit *u, const char *what, UnitDependency dep, Unit
                                          device, true, mask);
 }
 
+int unit_add_blockdev_dependency(Unit *u, const char *what, UnitDependencyMask mask) {
+        _cleanup_free_ char *escaped = NULL, *target = NULL;
+        int r;
+
+        assert(u);
+
+        if (isempty(what))
+                return 0;
+
+        if (!path_startswith(what, "/dev/"))
+                return 0;
+
+        /* If we don't support devices, then also don't bother with blockdev@.target */
+        if (!unit_type_supported(UNIT_DEVICE))
+                return 0;
+
+        r = unit_name_path_escape(what, &escaped);
+        if (r < 0)
+                return r;
+
+        r = unit_name_build("blockdev", escaped, ".target", &target);
+        if (r < 0)
+                return r;
+
+        return unit_add_dependency_by_name(u, UNIT_AFTER, target, true, mask);
+}
+
 int unit_coldplug(Unit *u) {
         int r = 0, q;
         char **i;
@@ -4272,6 +4333,9 @@ int unit_patch_contexts(Unit *u) {
                 if (ec->protect_kernel_logs)
                         ec->capability_bounding_set &= ~(UINT64_C(1) << CAP_SYSLOG);
 
+                if (ec->protect_clock)
+                        ec->capability_bounding_set &= ~((UINT64_C(1) << CAP_SYS_TIME) | (UINT64_C(1) << CAP_WAKE_ALARM));
+
                 if (ec->dynamic_user) {
                         if (!ec->user) {
                                 r = user_from_unit_name(u, &ec->user);
@@ -4330,6 +4394,12 @@ int unit_patch_contexts(Unit *u) {
                         if (r < 0)
                                 return r;
                 }
+
+                if (ec->protect_clock) {
+                        r = cgroup_add_device_allow(cc, "char-rtc", "r");
+                        if (r < 0)
+                                return r;
+                }
         }
 
         return 0;
@@ -5051,14 +5121,21 @@ static void unit_unref_uid_internal(
         *ref_uid = UID_INVALID;
 }
 
-void unit_unref_uid(Unit *u, bool destroy_now) {
+static void unit_unref_uid(Unit *u, bool destroy_now) {
         unit_unref_uid_internal(u, &u->ref_uid, destroy_now, manager_unref_uid);
 }
 
-void unit_unref_gid(Unit *u, bool destroy_now) {
+static void unit_unref_gid(Unit *u, bool destroy_now) {
         unit_unref_uid_internal(u, (uid_t*) &u->ref_gid, destroy_now, manager_unref_gid);
 }
 
+void unit_unref_uid_gid(Unit *u, bool destroy_now) {
+        assert(u);
+
+        unit_unref_uid(u, destroy_now);
+        unit_unref_gid(u, destroy_now);
+}
+
 static int unit_ref_uid_internal(
                 Unit *u,
                 uid_t *ref_uid,
@@ -5097,11 +5174,11 @@ static int unit_ref_uid_internal(
         return 1;
 }
 
-int unit_ref_uid(Unit *u, uid_t uid, bool clean_ipc) {
+static int unit_ref_uid(Unit *u, uid_t uid, bool clean_ipc) {
         return unit_ref_uid_internal(u, &u->ref_uid, uid, clean_ipc, manager_ref_uid);
 }
 
-int unit_ref_gid(Unit *u, gid_t gid, bool clean_ipc) {
+static int unit_ref_gid(Unit *u, gid_t gid, bool clean_ipc) {
         return unit_ref_uid_internal(u, (uid_t*) &u->ref_gid, (uid_t) gid, clean_ipc, manager_ref_gid);
 }
 
@@ -5146,13 +5223,6 @@ int unit_ref_uid_gid(Unit *u, uid_t uid, gid_t gid) {
         return r;
 }
 
-void unit_unref_uid_gid(Unit *u, bool destroy_now) {
-        assert(u);
-
-        unit_unref_uid(u, destroy_now);
-        unit_unref_gid(u, destroy_now);
-}
-
 void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid) {
         int r;
 
@@ -5318,7 +5388,7 @@ static void unit_update_dependency_mask(Unit *u, UnitDependency d, Unit *other,
         if (di.origin_mask == 0 && di.destination_mask == 0) {
                 /* No bit set anymore, let's drop the whole entry */
                 assert_se(hashmap_remove(u->dependencies[d], other));
-                log_unit_debug(u, "%s lost dependency %s=%s", u->id, unit_dependency_to_string(d), other->id);
+                log_unit_debug(u, "lost dependency %s=%s", unit_dependency_to_string(d), other->id);
         } else
                 /* Mask was reduced, let's update the entry */
                 assert_se(hashmap_update(u->dependencies[d], other, di.data) == 0);
@@ -5733,9 +5803,11 @@ bool unit_needs_console(Unit *u) {
         return exec_context_may_touch_console(ec);
 }
 
-const char *unit_label_path(Unit *u) {
+const char *unit_label_path(const Unit *u) {
         const char *p;
 
+        assert(u);
+
         /* Returns the file system path to use for MAC access decisions, i.e. the file to read the SELinux label off
          * when validating access checks. */
 
index c5d8170c920424943f3901359637fa4fccc1d549..2e103f7ab294d07fa5e5aad9dbccd1ffe9f74bc1 100644 (file)
@@ -9,6 +9,7 @@
 #include "condition.h"
 #include "emergency-action.h"
 #include "list.h"
+#include "show-status.h"
 #include "set.h"
 #include "unit-file.h"
 #include "cgroup.h"
@@ -381,6 +382,9 @@ typedef struct UnitStatusMessageFormats {
         const char *starting_stopping[2];
         const char *finished_start_job[_JOB_RESULT_MAX];
         const char *finished_stop_job[_JOB_RESULT_MAX];
+        /* If this entry is present, it'll be called to provide a context-dependent format string,
+         * or NULL to fall back to finished_{start,stop}_job; if those are NULL too, fall back to generic. */
+        const char *(*finished_job)(Unit *u, JobType t, JobResult result);
 } UnitStatusMessageFormats;
 
 /* Flags used when writing drop-in files or transient unit files */
@@ -530,7 +534,7 @@ typedef struct UnitVTable {
         void (*notify_message)(Unit *u, const struct ucred *ucred, char **tags, FDSet *fds);
 
         /* Called whenever a name this Unit registered for comes or goes away. */
-        void (*bus_name_owner_change)(Unit *u, const char *old_owner, const char *new_owner);
+        void (*bus_name_owner_change)(Unit *u, const char *new_owner);
 
         /* Called for each property that is being set */
         int (*bus_set_property)(Unit *u, const char *name, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
@@ -600,6 +604,15 @@ typedef struct UnitVTable {
         /* True if cgroup delegation is permissible */
         bool can_delegate:1;
 
+        /* True if the unit type triggers other units, i.e. can have a UNIT_TRIGGERS dependency */
+        bool can_trigger:1;
+
+        /* True if the unit type knows a failure state, and thus can be source of an OnFailure= dependency */
+        bool can_fail:1;
+
+        /* True if After= dependencies should be refused */
+        bool refuse_after:1;
+
         /* True if units of this type shall be startable only once and then never again */
         bool once_only:1;
 
@@ -734,11 +747,12 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds);
 int unit_deserialize_skip(FILE *f);
 
 int unit_add_node_dependency(Unit *u, const char *what, UnitDependency d, UnitDependencyMask mask);
+int unit_add_blockdev_dependency(Unit *u, const char *what, UnitDependencyMask mask);
 
 int unit_coldplug(Unit *u);
 void unit_catchup(Unit *u);
 
-void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) _printf_(3, 0);
+void unit_status_printf(Unit *u, StatusType status_type, const char *status, const char *unit_status_msg_format) _printf_(4, 0);
 
 bool unit_need_daemon_reload(Unit *u);
 
@@ -806,12 +820,6 @@ int unit_fail_if_noncanonical(Unit *u, const char* where);
 
 int unit_test_start_limit(Unit *u);
 
-void unit_unref_uid(Unit *u, bool destroy_now);
-int unit_ref_uid(Unit *u, uid_t uid, bool clean_ipc);
-
-void unit_unref_gid(Unit *u, bool destroy_now);
-int unit_ref_gid(Unit *u, gid_t gid, bool clean_ipc);
-
 int unit_ref_uid_gid(Unit *u, uid_t uid, gid_t gid);
 void unit_unref_uid_gid(Unit *u, bool destroy_now);
 
@@ -838,7 +846,7 @@ int unit_warn_leftover_processes(Unit *u);
 
 bool unit_needs_console(Unit *u);
 
-const char *unit_label_path(Unit *u);
+const char *unit_label_path(const Unit *u);
 
 int unit_pid_attachable(Unit *unit, pid_t pid, sd_bus_error *error);
 
index b87abe9ece0571de45e0347474ed0cabce47cda9..1deab765fbb94070fa4a247c99d4863ba4af2ea6 100644 (file)
@@ -99,7 +99,14 @@ static int split_keyspec(const char *keyspec, char **ret_keyfile, char **ret_key
         return 0;
 }
 
-static int generate_keydev_mount(const char *name, const char *keydev, const char *keydev_timeout, bool canfail, char **unit, char **mount) {
+static int generate_keydev_mount(
+                const char *name,
+                const char *keydev,
+                const char *keydev_timeout,
+                bool canfail,
+                char **unit,
+                char **mount) {
+
         _cleanup_free_ char *u = NULL, *where = NULL, *name_escaped = NULL, *device_unit = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         int r;
@@ -223,8 +230,8 @@ static int create_disk(
                 const char *options) {
 
         _cleanup_free_ char *n = NULL, *d = NULL, *u = NULL, *e = NULL,
-                *keydev_mount = NULL, *keyfile_timeout_value = NULL, *password_escaped = NULL,
-                *filtered = NULL, *u_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL, *header_path = NULL;
+                *keydev_mount = NULL, *keyfile_timeout_value = NULL,
+                *filtered = NULL, *u_escaped = NULL, *name_escaped = NULL, *header_path = NULL, *password_buffer = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         const char *dmname;
         bool noauto, nofail, tmp, swap, netdev, attach_in_initrd;
@@ -285,39 +292,29 @@ static int create_disk(
         if (r < 0)
                 return r;
 
-        fprintf(f,
-                "[Unit]\n"
-                "Description=Cryptography Setup for %%I\n"
-                "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
-                "SourcePath=%s\n"
-                "DefaultDependencies=no\n"
-                "IgnoreOnIsolate=true\n"
-                "After=%s\n",
-                arg_crypttab,
-                netdev ? "remote-fs-pre.target" : "cryptsetup-pre.target");
+        r = generator_write_cryptsetup_unit_section(f, arg_crypttab);
+        if (r < 0)
+                return r;
+
+        if (netdev)
+                fprintf(f, "After=remote-fs-pre.target\n");
 
         /* If initrd takes care of attaching the disk then it should also detach it during shutdown. */
         if (!attach_in_initrd)
                 fprintf(f, "Conflicts=umount.target\n");
 
-        if (password) {
-                password_escaped = specifier_escape(password);
-                if (!password_escaped)
-                        return log_oom();
-        }
-
         if (keydev) {
-                _cleanup_free_ char *unit = NULL, *p = NULL;
+                _cleanup_free_ char *unit = NULL;
 
                 r = generate_keydev_mount(name, keydev, keyfile_timeout_value, keyfile_can_timeout > 0, &unit, &keydev_mount);
                 if (r < 0)
                         return log_error_errno(r, "Failed to generate keydev mount unit: %m");
 
-                p = path_join(keydev_mount, password_escaped);
-                if (!p)
+                password_buffer = path_join(keydev_mount, password);
+                if (!password_buffer)
                         return log_oom();
 
-                free_and_replace(password_escaped, p);
+                password = password_buffer;
 
                 fprintf(f, "After=%s\n", unit);
                 if (keyfile_can_timeout > 0)
@@ -344,17 +341,13 @@ static int create_disk(
                         return r;
         }
 
-        if (path_startswith(u, "/dev/")) {
+        if (path_startswith(u, "/dev/"))
                 fprintf(f,
                         "BindsTo=%s\n"
                         "After=%s\n"
                         "Before=umount.target\n",
                         d, d);
-
-                if (swap)
-                        fputs("Before=dev-mapper-%i.swap\n",
-                              f);
-        } else
+        else
                 /* For loopback devices, add systemd-tmpfiles-setup-dev.service
                    dependency to ensure that loopback support is available in
                    the kernel (/dev/loop-control needs to exist) */
@@ -368,23 +361,9 @@ static int create_disk(
         if (r < 0)
                 log_warning_errno(r, "Failed to write device timeout drop-in: %m");
 
-        if (filtered) {
-                filtered_escaped = specifier_escape(filtered);
-                if (!filtered_escaped)
-                        return log_oom();
-        }
-
-        fprintf(f,
-                "\n[Service]\n"
-                "Type=oneshot\n"
-                "RemainAfterExit=yes\n"
-                "TimeoutSec=0\n" /* the binary handles timeouts anyway */
-                "KeyringMode=shared\n" /* make sure we can share cached keys among instances */
-                "OOMScoreAdjust=500\n" /* unlocking can allocate a lot of memory if Argon2 is used */
-                "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
-                "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
-                name_escaped, u_escaped, strempty(password_escaped), strempty(filtered_escaped),
-                name_escaped);
+        r = generator_write_cryptsetup_service_section(f, name, u, password, filtered);
+        if (r < 0)
+                return r;
 
         if (tmp)
                 fprintf(f,
index c1be6c034c5619714e0992283294c345c12ecbbf..e1418419f7b628b221075440ce4bac8cf1c970e6 100644 (file)
@@ -11,6 +11,7 @@
 #include "log.h"
 #include "loop-util.h"
 #include "main-func.h"
+#include "parse-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "user-util.h"
@@ -22,7 +23,7 @@ static enum {
 } arg_action = ACTION_DISSECT;
 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;
+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 size_t arg_root_hash_size = 0;
 
@@ -36,6 +37,7 @@ static void help(void) {
                "     --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",
                program_invocation_short_name,
@@ -48,6 +50,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_VERSION = 0x100,
                 ARG_DISCARD,
                 ARG_ROOT_HASH,
+                ARG_FSCK,
         };
 
         static const struct option options[] = {
@@ -57,6 +60,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "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      },
                 {}
         };
 
@@ -123,6 +127,14 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
+                case ARG_FSCK:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --fsck= parameter: %s", optarg);
+
+                        SET_FLAG(arg_flags, DISSECT_IMAGE_FSCK, r);
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -261,6 +273,8 @@ static int run(int argc, char *argv[]) {
                         return r;
 
                 r = dissected_image_mount(m, arg_path, UID_INVALID, arg_flags);
+                if (r == -EUCLEAN)
+                        return log_error_errno(r, "File system check on image failed: %m");
                 if (r < 0)
                         return log_error_errno(r, "Failed to mount image: %m");
 
index 528e6452cf41eac5d3e769e3497b61d3c87c47c7..901fbf081591347bc2b480ed7097ee3b9f24dd2a 100644 (file)
@@ -4,19 +4,6 @@
 #include <getopt.h>
 #include <unistd.h>
 
-#if HAVE_CRYPT_H
-/* libxcrypt is a replacement for glibc's libcrypt, and libcrypt might be
- * removed from glibc at some point. As part of the removal, defines for
- * crypt(3) are dropped from unistd.h, and we must include crypt.h instead.
- *
- * Newer versions of glibc (v2.0+) already ship crypt.h with a definition
- * of crypt(3) as well, so we simply include it if it is present.  MariaDB,
- * MySQL, PostgreSQL, Perl and some other wide-spread packages do it the
- * same way since ages without any problems.
- */
-#  include <crypt.h>
-#endif
-
 #include "sd-id128.h"
 
 #include "alloc-util.h"
@@ -28,6 +15,7 @@
 #include "fs-util.h"
 #include "hostname-util.h"
 #include "kbd-util.h"
+#include "libcrypt-util.h"
 #include "locale-util.h"
 #include "main-func.h"
 #include "memory-util.h"
@@ -562,10 +550,9 @@ static int prompt_root_password(void) {
                 r = ask_password_tty(-1, msg1, NULL, 0, 0, NULL, &a);
                 if (r < 0)
                         return log_error_errno(r, "Failed to query root password: %m");
-                if (strv_length(a) != 1) {
-                        log_warning("Received multiple passwords, where we expected one.");
-                        return -EINVAL;
-                }
+                if (strv_length(a) != 1)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                               "Received multiple passwords, where we expected one.");
 
                 if (isempty(*a)) {
                         log_warning("No password entered, skipping.");
@@ -575,6 +562,9 @@ static int prompt_root_password(void) {
                 r = ask_password_tty(-1, msg2, NULL, 0, 0, NULL, &b);
                 if (r < 0)
                         return log_error_errno(r, "Failed to query root password: %m");
+                if (strv_length(b) != 1)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                               "Received multiple passwords, where we expected one.");
 
                 if (!streq(*a, *b)) {
                         log_error("Entered passwords did not match, please try again.");
index 6fd6fbdf1ccdf542b8cf6ae6b7ac3a16d36ff723..5a0a871759ed467df723546f99e5351a1c794b9a 100644 (file)
@@ -118,11 +118,18 @@ static int add_swap(
 
         fprintf(f,
                 "[Unit]\n"
-                "SourcePath=%s\n"
-                "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n"
-                "[Swap]\n",
+                "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n"
+                "SourcePath=%s\n",
                 fstab_path());
 
+        r = generator_write_blockdev_dependency(f, what);
+        if (r < 0)
+                return r;
+
+        fprintf(f,
+                "\n"
+                "[Swap]\n");
+
         r = write_what(f, what);
         if (r < 0)
                 return r;
@@ -174,8 +181,13 @@ static bool mount_in_initrd(struct mntent *me) {
                streq(me->mnt_dir, "/usr");
 }
 
-static int write_timeout(FILE *f, const char *where, const char *opts,
-                         const char *filter, const char *variable) {
+static int write_timeout(
+                FILE *f,
+                const char *where,
+                const char *opts,
+                const char *filter,
+                const char *variable) {
+
         _cleanup_free_ char *timeout = NULL;
         char timespan[FORMAT_TIMESPAN_MAX];
         usec_t u;
@@ -208,8 +220,12 @@ static int write_mount_timeout(FILE *f, const char *where, const char *opts) {
                              "x-systemd.mount-timeout\0", "TimeoutSec");
 }
 
-static int write_dependency(FILE *f, const char *opts,
-                const char *filter, const char *format) {
+static int write_dependency(
+                FILE *f,
+                const char *opts,
+                const char *filter,
+                const char *format) {
+
         _cleanup_strv_free_ char **names = NULL, **units = NULL;
         _cleanup_free_ char *res = NULL;
         char **s;
@@ -230,6 +246,7 @@ static int write_dependency(FILE *f, const char *opts,
                 r = unit_name_mangle_with_suffix(*s, "as dependency", 0, ".mount", &x);
                 if (r < 0)
                         return log_error_errno(r, "Failed to generate unit name: %m");
+
                 r = strv_consume(&units, x);
                 if (r < 0)
                         return log_oom();
@@ -249,7 +266,8 @@ static int write_dependency(FILE *f, const char *opts,
 }
 
 static int write_after(FILE *f, const char *opts) {
-        return write_dependency(f, opts, "x-systemd.after", "After=%1$s\n");
+        return write_dependency(f, opts,
+                                "x-systemd.after", "After=%1$s\n");
 }
 
 static int write_requires_after(FILE *f, const char *opts) {
@@ -306,6 +324,7 @@ static int add_mount(
                 *automount_name = NULL,
                 *filtered = NULL,
                 *where_escaped = NULL;
+        _cleanup_strv_free_ char **wanted_by = NULL, **required_by = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         int r;
 
@@ -327,6 +346,14 @@ static int add_mount(
             mount_point_ignore(where))
                 return 0;
 
+        r = fstab_extract_values(opts, "x-systemd.wanted-by", &wanted_by);
+        if (r < 0)
+                return r;
+
+        r = fstab_extract_values(opts, "x-systemd.required-by", &required_by);
+        if (r < 0)
+                return r;
+
         if (path_equal(where, "/")) {
                 if (flags & NOAUTO)
                         log_warning("Ignoring \"noauto\" for root device");
@@ -334,7 +361,13 @@ static int add_mount(
                         log_warning("Ignoring \"nofail\" for root device");
                 if (flags & AUTOMOUNT)
                         log_warning("Ignoring automount option for root device");
+                if (!strv_isempty(wanted_by))
+                        log_warning("Ignoring \"x-systemd.wanted-by=\" for root device");
+                if (!strv_isempty(required_by))
+                        log_warning("Ignoring \"x-systemd.required-by=\" for root device");
 
+                required_by = strv_free(required_by);
+                wanted_by = strv_free(wanted_by);
                 SET_FLAG(flags, NOAUTO | NOFAIL | AUTOMOUNT, false);
         }
 
@@ -348,8 +381,8 @@ static int add_mount(
 
         fprintf(f,
                 "[Unit]\n"
-                "SourcePath=%s\n"
-                "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
+                "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n"
+                "SourcePath=%s\n",
                 source);
 
         /* All mounts under /sysroot need to happen later, at initrd-fs.target time. IOW, it's not
@@ -396,7 +429,14 @@ static int add_mount(
                         return r;
         }
 
-        fprintf(f, "\n[Mount]\n");
+        r = generator_write_blockdev_dependency(f, what);
+        if (r < 0)
+                return r;
+
+        fprintf(f,
+                "\n"
+                "[Mount]\n");
+
         if (original_where)
                 fprintf(f, "# Canonicalized from %s\n", original_where);
 
@@ -451,14 +491,28 @@ static int add_mount(
                         return r;
         }
 
-        if (!(flags & NOAUTO) && !(flags & AUTOMOUNT)) {
-                r = generator_add_symlink(dest, post,
-                                          (flags & NOFAIL) ? "wants" : "requires", name);
-                if (r < 0)
-                        return r;
-        }
-
-        if (flags & AUTOMOUNT) {
+        if (!FLAGS_SET(flags, AUTOMOUNT)) {
+                if (!FLAGS_SET(flags, NOAUTO) && strv_isempty(wanted_by) && strv_isempty(required_by)) {
+                        r = generator_add_symlink(dest, post,
+                                                  (flags & NOFAIL) ? "wants" : "requires", name);
+                        if (r < 0)
+                                return r;
+                } else {
+                        char **s;
+
+                        STRV_FOREACH(s, wanted_by) {
+                                r = generator_add_symlink(dest, *s, "wants", name);
+                                if (r < 0)
+                                        return r;
+                        }
+
+                        STRV_FOREACH(s, required_by) {
+                                r = generator_add_symlink(dest, *s, "requires", name);
+                                if (r < 0)
+                                        return r;
+                        }
+                }
+        } else {
                 r = unit_name_from_path(where, ".automount", &automount_name);
                 if (r < 0)
                         return log_error_errno(r, "Failed to generate unit name: %m");
index aca82edad9628b465fa0c61b077c34c39da69ad7..3d1ac7e68a03ed56bb00d8e1a7644b0fef84e16a 100644 (file)
@@ -36,7 +36,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
         if (getenv_bool("SYSTEMD_FUZZ_OUTPUT") <= 0)
                 assert_se(g = open_memstream_unlocked(&out, &out_size));
 
-        bus_message_dump(m, g ?: stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+        sd_bus_message_dump(m, g ?: stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
 
         r = sd_bus_message_rewind(m, true);
         assert_se(r >= 0);
index af3bd2345041a8563ef9f7d9d55efb3eb7fe4413..2067eeaf89afb403e7f4a3e7d50485da3eb15a39 100644 (file)
@@ -105,9 +105,8 @@ static int open_parent_block_device(dev_t devnum, int *ret_fd) {
 }
 
 static int add_cryptsetup(const char *id, const char *what, bool rw, bool require, char **device) {
-        _cleanup_free_ char *e = NULL, *n = NULL, *d = NULL, *id_escaped = NULL, *what_escaped = NULL;
+        _cleanup_free_ char *e = NULL, *n = NULL, *d = NULL;
         _cleanup_fclose_ FILE *f = NULL;
-        const char *p;
         int r;
 
         assert(id);
@@ -125,44 +124,28 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, bool requir
         if (r < 0)
                 return log_error_errno(r, "Failed to generate unit name: %m");
 
-        id_escaped = specifier_escape(id);
-        if (!id_escaped)
-                return log_oom();
-
-        what_escaped = specifier_escape(what);
-        if (!what_escaped)
-                return log_oom();
+        r = generator_open_unit_file(arg_dest, NULL, n, &f);
+        if (r < 0)
+                return r;
 
-        p = prefix_roota(arg_dest, n);
-        f = fopen(p, "wxe");
-        if (!f)
-                return log_error_errno(errno, "Failed to create unit file %s: %m", p);
+        r = generator_write_cryptsetup_unit_section(f, NULL);
+        if (r < 0)
+                return r;
 
         fprintf(f,
-                "# Automatically generated by systemd-gpt-auto-generator\n\n"
-                "[Unit]\n"
-                "Description=Cryptography Setup for %%I\n"
-                "Documentation=man:systemd-gpt-auto-generator(8) man:systemd-cryptsetup@.service(8)\n"
-                "DefaultDependencies=no\n"
-                "Conflicts=umount.target\n"
-                "BindsTo=dev-mapper-%%i.device %s\n"
                 "Before=umount.target cryptsetup.target\n"
-                "After=%s\n"
-                "IgnoreOnIsolate=true\n"
-                "[Service]\n"
-                "Type=oneshot\n"
-                "RemainAfterExit=yes\n"
-                "TimeoutSec=0\n" /* the binary handles timeouts anyway */
-                "KeyringMode=shared\n" /* make sure we can share cached keys among instances */
-                "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '' '%s'\n"
-                "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
-                d, d,
-                id_escaped, what_escaped, rw ? "" : "read-only",
-                id_escaped);
+                "Conflicts=umount.target\n"
+                "BindsTo=%s\n"
+                "After=%s\n",
+                d, d);
+
+        r = generator_write_cryptsetup_service_section(f, id, what, NULL, rw ? NULL : "read-only");
+        if (r < 0)
+                return r;
 
         r = fflush_and_check(f);
         if (r < 0)
-                return log_error_errno(r, "Failed to write file %s: %m", p);
+                return log_error_errno(r, "Failed to write file %s: %m", n);
 
         r = generator_add_symlink(arg_dest, d, "wants", n);
         if (r < 0)
@@ -227,7 +210,6 @@ static int add_mount(
         log_debug("Adding %s: %s fstype=%s", where, what, fstype ?: "(any)");
 
         if (streq_ptr(fstype, "crypto_LUKS")) {
-
                 r = add_cryptsetup(id, what, rw, true, &crypto_what);
                 if (r < 0)
                         return r;
@@ -262,6 +244,10 @@ static int add_mount(
         if (r < 0)
                 return r;
 
+        r = generator_write_blockdev_dependency(f, what);
+        if (r < 0)
+                return r;
+
         fprintf(f,
                 "\n"
                 "[Mount]\n"
@@ -370,7 +356,14 @@ static int add_swap(const char *path) {
                 "# Automatically generated by systemd-gpt-auto-generator\n\n"
                 "[Unit]\n"
                 "Description=Swap Partition\n"
-                "Documentation=man:systemd-gpt-auto-generator(8)\n\n"
+                "Documentation=man:systemd-gpt-auto-generator(8)\n");
+
+        r = generator_write_blockdev_dependency(f, path);
+        if (r < 0)
+                return r;
+
+        fprintf(f,
+                "\n"
                 "[Swap]\n"
                 "What=%s\n",
                 path);
@@ -710,6 +703,18 @@ static int enumerate_partitions(dev_t devnum) {
                         r = k;
         }
 
+        if (m->partitions[PARTITION_VAR].found) {
+                k = add_partition_mount(m->partitions + PARTITION_VAR, "var", "/var", "Variable Data Partition");
+                if (k < 0)
+                        r = k;
+        }
+
+        if (m->partitions[PARTITION_TMP].found) {
+                k = add_partition_mount(m->partitions + PARTITION_TMP, "var-tmp", "/var/tmp", "Temporary Data Partition");
+                if (k < 0)
+                        r = k;
+        }
+
         if (m->partitions[PARTITION_ROOT].found) {
                 k = add_root_rw(m->partitions + PARTITION_ROOT);
                 if (k < 0)
index 5e40933fdf28d47afc8927a854e34bc3e932b7a6..95af8efa7e2e96ff5ca3fc105fe25dcc847dd78a 100644 (file)
@@ -93,7 +93,7 @@ static int process_resume(void) {
                 return log_error_errno(r, "Failed to generate unit name: %m");
 
         r = write_drop_in(arg_dest, device_unit, 40, "device-timeout",
-                          "# Automatically generated by systemd-cryptsetup-generator\n\n"
+                          "# Automatically generated by systemd-hibernate-resume-generator\n\n"
                           "[Unit]\nJobTimeoutSec=0");
         if (r < 0)
                 log_warning_errno(r, "Failed to write device timeout drop-in: %m");
diff --git a/src/home/home-util.c b/src/home/home-util.c
new file mode 100644 (file)
index 0000000..a53b0d3
--- /dev/null
@@ -0,0 +1,160 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "dns-domain.h"
+#include "errno-util.h"
+#include "home-util.h"
+#include "libcrypt-util.h"
+#include "memory-util.h"
+#include "path-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "user-util.h"
+
+bool suitable_user_name(const char *name) {
+
+        /* Checks whether the specified name is suitable for management via homed. Note that client-side
+         * we usually validate with the simple valid_user_group_name(), while server-side we are a bit more
+         * restrictive, so that we can change the rules server-side without having to update things
+         * client-side too. */
+
+        if (!valid_user_group_name(name))
+                return false;
+
+        /* We generally rely on NSS to tell us which users not to care for, but let's filter out some
+         * particularly well-known users. */
+        if (STR_IN_SET(name,
+                       "root",
+                       "nobody",
+                       NOBODY_USER_NAME, NOBODY_GROUP_NAME))
+                return false;
+
+        /* Let's also defend our own namespace, as well as Debian's (unwritten?) logic of prefixing system
+         * users with underscores. */
+        if (STARTSWITH_SET(name, "systemd-", "_"))
+                return false;
+
+        return true;
+}
+
+int suitable_realm(const char *realm) {
+        _cleanup_free_ char *normalized = NULL;
+        int r;
+
+        /* Similar to the above: let's validate the realm a bit stricter server-side than client side */
+
+        r = dns_name_normalize(realm, 0, &normalized); /* this also checks general validity */
+        if (r == -EINVAL)
+                return 0;
+        if (r < 0)
+                return r;
+
+        if (!streq(realm, normalized)) /* is this normalized? */
+                return false;
+
+        if (dns_name_is_root(realm)) /* Don't allow top level domain */
+                return false;
+
+        return true;
+}
+
+int suitable_image_path(const char *path) {
+
+        return !empty_or_root(path) &&
+                path_is_valid(path) &&
+                path_is_absolute(path);
+}
+
+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;
+        int r;
+
+        assert(t);
+        assert(ret_user_name);
+        assert(ret_realm);
+
+        c = strchr(t, '@');
+        if (!c) {
+                user_name = strdup(t);
+                if (!user_name)
+                        return -ENOMEM;
+        } else {
+                user_name = strndup(t, c - t);
+                if (!user_name)
+                        return -ENOMEM;
+
+                realm = strdup(c + 1);
+                if (!realm)
+                        return -ENOMEM;
+        }
+
+        if (!suitable_user_name(user_name))
+                return -EINVAL;
+
+        if (realm) {
+                r = suitable_realm(realm);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        return -EINVAL;
+        }
+
+        *ret_user_name = TAKE_PTR(user_name);
+        *ret_realm = TAKE_PTR(realm);
+
+        return 0;
+}
+
+int bus_message_append_secret(sd_bus_message *m, UserRecord *secret) {
+        _cleanup_(erase_and_freep) char *formatted = NULL;
+        JsonVariant *v;
+        int r;
+
+        assert(m);
+        assert(secret);
+
+        if (!FLAGS_SET(secret->mask, USER_RECORD_SECRET))
+                return sd_bus_message_append(m, "s", "{}");
+
+        v = json_variant_by_key(secret->json, "secret");
+        if (!v)
+                return -EINVAL;
+
+        r = json_variant_format(v, 0, &formatted);
+        if (r < 0)
+                return r;
+
+        return sd_bus_message_append(m, "s", formatted);
+}
+
+int test_password_one(const char *hashed_password, const char *password) {
+        struct crypt_data cc = {};
+        const char *k;
+        bool b;
+
+        errno = 0;
+        k = crypt_r(password, hashed_password, &cc);
+        if (!k) {
+                explicit_bzero_safe(&cc, sizeof(cc));
+                return errno_or_else(EINVAL);
+        }
+
+        b = streq(k, hashed_password);
+        explicit_bzero_safe(&cc, sizeof(cc));
+        return b;
+}
+
+int test_password_many(char **hashed_password, const char *password) {
+        char **hpw;
+        int r;
+
+        STRV_FOREACH(hpw, hashed_password) {
+                r = test_password_one(*hpw, password);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        return true;
+        }
+
+        return false;
+}
diff --git a/src/home/home-util.h b/src/home/home-util.h
new file mode 100644 (file)
index 0000000..df20c0a
--- /dev/null
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <stdbool.h>
+
+#include "sd-bus.h"
+
+#include "time-util.h"
+#include "user-record.h"
+
+bool suitable_user_name(const char *name);
+int suitable_realm(const char *realm);
+int suitable_image_path(const char *path);
+
+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 */
+#define HOME_SLOW_BUS_CALL_TIMEOUT_USEC (2*USEC_PER_MINUTE)
+
+int test_password_one(const char *hashed_password, const char *password);
+int test_password_many(char **hashed_password, const char *password);
diff --git a/src/home/homectl.c b/src/home/homectl.c
new file mode 100644 (file)
index 0000000..d35034a
--- /dev/null
@@ -0,0 +1,3607 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <getopt.h>
+
+#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 "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 "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"
+#include "pkcs11-util.h"
+#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"
+#include "user-record-show.h"
+#include "user-record-util.h"
+#include "user-record.h"
+#include "user-util.h"
+#include "verbs.h"
+
+static PagerFlags arg_pager_flags = 0;
+static bool arg_legend = true;
+static bool arg_ask_password = true;
+static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
+static const char *arg_host = NULL;
+static const char *arg_identity = NULL;
+static JsonVariant *arg_identity_extra = NULL;
+static JsonVariant *arg_identity_extra_privileged = NULL;
+static JsonVariant *arg_identity_extra_this_machine = NULL;
+static JsonVariant *arg_identity_extra_rlimits = NULL;
+static char **arg_identity_filter = NULL; /* this one is also applied to 'privileged' and 'thisMachine' subobjects */
+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 bool arg_json = false;
+static JsonFormatFlags arg_json_format_flags = 0;
+static bool arg_and_resize = false;
+static bool arg_and_change_password = false;
+static enum {
+        EXPORT_FORMAT_FULL,          /* export the full record */
+        EXPORT_FORMAT_STRIPPED,      /* strip "state" + "binding", but leave signature in place */
+        EXPORT_FORMAT_MINIMAL,       /* also strip signature */
+} arg_export_format = EXPORT_FORMAT_FULL;
+
+STATIC_DESTRUCTOR_REGISTER(arg_identity_extra, json_variant_unrefp);
+STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_this_machine, json_variant_unrefp);
+STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_privileged, json_variant_unrefp);
+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 bool identity_properties_specified(void) {
+        return
+                arg_identity ||
+                !json_variant_is_blank_object(arg_identity_extra) ||
+                !json_variant_is_blank_object(arg_identity_extra_privileged) ||
+                !json_variant_is_blank_object(arg_identity_extra_this_machine) ||
+                !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);
+}
+
+static int acquire_bus(sd_bus **bus) {
+        int r;
+
+        assert(bus);
+
+        if (*bus)
+                return 0;
+
+        r = bus_connect_transport(arg_transport, arg_host, false, bus);
+        if (r < 0)
+                return log_error_errno(r, "Failed to connect to bus: %m");
+
+        (void) sd_bus_set_allow_interactive_authorization(*bus, arg_ask_password);
+
+        return 0;
+}
+
+static int list_homes(int argc, char *argv[], void *userdata) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        _cleanup_(table_unrefp) Table *table = NULL;
+        int r;
+
+        (void) pager_open(arg_pager_flags);
+
+        r = acquire_bus(&bus);
+        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);
+        if (r < 0)
+                return log_error_errno(r, "Failed to list homes: %s", bus_error_message(&error, r));
+
+        table = table_new("name", "uid", "gid", "state", "realname", "home", "shell");
+        if (!table)
+                return log_oom();
+
+        r = sd_bus_message_enter_container(reply, 'a', "(susussso)");
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        for (;;) {
+                const char *name, *state, *realname, *home, *shell, *color;
+                TableCell *cell;
+                uint32_t uid, gid;
+
+                r = sd_bus_message_read(reply, "(susussso)", &name, &uid, &state, &gid, &realname, &home, &shell, NULL);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+                if (r == 0)
+                        break;
+
+                r = table_add_many(table,
+                                   TABLE_STRING, name,
+                                   TABLE_UID, uid,
+                                   TABLE_GID, gid);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add row to table: %m");
+
+
+                r = table_add_cell(table, &cell, TABLE_STRING, state);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add field to table: %m");
+
+                color = user_record_state_color(state);
+                if (color)
+                        (void) table_set_color(table, cell, color);
+
+                r = table_add_many(table,
+                                   TABLE_STRING, strna(empty_to_null(realname)),
+                                   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");
+        }
+
+        r = sd_bus_message_exit_container(reply);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        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");
+
+                table_set_header(table, arg_legend);
+
+                if (arg_json)
+                        r = table_print_json(table, stdout, arg_json_format_flags);
+                else
+                        r = table_print(table, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to show table: %m");
+        }
+
+        if (arg_legend && !arg_json) {
+                if (table_get_rows(table) > 1)
+                        printf("\n%zu homes listed.\n", table_get_rows(table) - 1);
+                else
+                        printf("No homes.\n");
+        }
+
+        return 0;
+}
+
+static int acquire_existing_password(const char *user_name, UserRecord *hr, bool emphasize_current) {
+        _cleanup_(strv_free_erasep) char **password = NULL;
+        _cleanup_free_ char *question = NULL;
+        char *e;
+        int r;
+
+        assert(user_name);
+        assert(hr);
+
+        e = getenv("PASSWORD");
+        if (e) {
+                /* People really shouldn't use environment variables for passing passwords. We support this
+                 * only for testing purposes, and do not document the behaviour, so that people won't
+                 * actually use this outside of testing. */
+
+                r = user_record_set_password(hr, STRV_MAKE(e), true);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to store password: %m");
+
+                string_erase(e);
+
+                if (unsetenv("PASSWORD") < 0)
+                        return log_error_errno(errno, "Failed to unset $PASSWORD: %m");
+
+                return 0;
+        }
+
+        if (asprintf(&question, emphasize_current ?
+                     "Please enter current password for user %s:" :
+                     "Please enter password for user %s:",
+                     user_name) < 0)
+                return log_oom();
+
+        r = ask_password_auto(question, "user-home", NULL, "home-password", USEC_INFINITY, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, &password);
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire password: %m");
+
+        r = user_record_set_password(hr, password, true);
+        if (r < 0)
+                return log_error_errno(r, "Failed to store password: %m");
+
+        return 0;
+}
+
+static int acquire_pkcs11_pin(const char *user_name, UserRecord *hr) {
+        _cleanup_(strv_free_erasep) char **pin = NULL;
+        _cleanup_free_ char *question = NULL;
+        char *e;
+        int r;
+
+        assert(user_name);
+        assert(hr);
+
+        e = getenv("PIN");
+        if (e) {
+                r = user_record_set_pkcs11_pin(hr, STRV_MAKE(e), false);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to store PKCS#11 PIN: %m");
+
+                string_erase(e);
+
+                if (unsetenv("PIN") < 0)
+                        return log_error_errno(errno, "Failed to unset $PIN: %m");
+
+                return 0;
+        }
+
+        if (asprintf(&question, "Please enter security token PIN for user %s:", user_name) < 0)
+                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);
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire security token PIN: %m");
+
+        r = user_record_set_pkcs11_pin(hr, pin, false);
+        if (r < 0)
+                return log_error_errno(r, "Failed to store security token PIN: %m");
+
+        return 0;
+}
+
+static int handle_generic_user_record_error(
+                const char *user_name,
+                UserRecord *hr,
+                const sd_bus_error *error,
+                int ret,
+                bool emphasize_current_password) {
+        int r;
+
+        assert(user_name);
+        assert(hr);
+
+        if (sd_bus_error_has_name(error, BUS_ERROR_HOME_ABSENT))
+                return log_error_errno(SYNTHETIC_ERRNO(EREMOTE),
+                                       "Home of user %s is currently absent, please plug in the necessary stroage device or backing file system.", user_name);
+
+        else if (sd_bus_error_has_name(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT))
+                return log_error_errno(SYNTHETIC_ERRNO(ETOOMANYREFS),
+                                       "Too frequent unsuccessful login attempts for user %s, try again later.", user_name);
+
+        else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_PASSWORD)) {
+
+                if (!strv_isempty(hr->password))
+                        log_notice("Password incorrect or not sufficient, please try again.");
+
+                r = acquire_existing_password(user_name, hr, emphasize_current_password);
+                if (r < 0)
+                        return r;
+
+        } else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN)) {
+
+                if (strv_isempty(hr->password))
+                        log_notice("Security token not inserted, please enter password.");
+                else
+                        log_notice("Password incorrect or not sufficient, and configured security token not inserted, please try again.");
+
+                r = acquire_existing_password(user_name, hr, emphasize_current_password);
+                if (r < 0)
+                        return r;
+
+        } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_NEEDED)) {
+
+                r = acquire_pkcs11_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.");
+
+                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_PIN_LOCKED))
+                return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Security token PIN is locked, please unlock security token PIN first.");
+
+        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);
+                if (r < 0)
+                        return r;
+
+        } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT)) {
+
+                log_notice("Security token PIN incorrect, please try again (only a few tries left!).");
+
+                r = acquire_pkcs11_pin(user_name, hr);
+                if (r < 0)
+                        return r;
+
+        } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT)) {
+
+                log_notice("Security token PIN incorrect, please try again (only one try left!).");
+
+                r = acquire_pkcs11_pin(user_name, hr);
+                if (r < 0)
+                        return r;
+        } else
+                return log_error_errno(ret, "Operation on home %s failed: %s", user_name, bus_error_message(error, ret));
+
+        return 0;
+}
+
+static int activate_home(int argc, char *argv[], void *userdata) {
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        int r, ret = 0;
+        char **i;
+
+        r = acquire_bus(&bus);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(i, strv_skip(argv, 1)) {
+                _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+
+                secret = user_record_new();
+                if (!secret)
+                        return log_oom();
+
+                for (;;) {
+                        _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");
+                        if (r < 0)
+                                return bus_log_create_error(r);
+
+                        r = sd_bus_message_append(m, "s", *i);
+                        if (r < 0)
+                                return bus_log_create_error(r);
+
+                        r = bus_message_append_secret(m, secret);
+                        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) {
+                                r = handle_generic_user_record_error(*i, secret, &error, r, false);
+                                if (r < 0) {
+                                        if (ret == 0)
+                                                ret = r;
+
+                                        break;
+                                }
+                        } else
+                                break;
+                }
+        }
+
+        return ret;
+}
+
+static int deactivate_home(int argc, char *argv[], void *userdata) {
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        int r, ret = 0;
+        char **i;
+
+        r = acquire_bus(&bus);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(i, strv_skip(argv, 1)) {
+                _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");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append(m, "s", *i);
+                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) {
+                        log_error_errno(r, "Failed to deactivate user home: %s", bus_error_message(&error, r));
+                        if (ret == 0)
+                                ret = r;
+                }
+        }
+
+        return ret;
+}
+
+static void dump_home_record(UserRecord *hr) {
+        int r;
+
+        assert(hr);
+
+        if (hr->incomplete) {
+                fflush(stdout);
+                log_warning("Warning: lacking rights to acquire privileged fields of user record of '%s', output incomplete.", hr->user_name);
+        }
+
+        if (arg_json) {
+                _cleanup_(user_record_unrefp) UserRecord *stripped = NULL;
+
+                if (arg_export_format == EXPORT_FORMAT_STRIPPED)
+                        r = user_record_clone(hr, USER_RECORD_EXTRACT_EMBEDDED, &stripped);
+                else if (arg_export_format == EXPORT_FORMAT_MINIMAL)
+                        r = user_record_clone(hr, USER_RECORD_EXTRACT_SIGNABLE, &stripped);
+                else
+                        r = 0;
+                if (r < 0)
+                        log_warning_errno(r, "Failed to strip user record, ignoring: %m");
+                if (stripped)
+                        hr = stripped;
+
+                json_variant_dump(hr->json, arg_json_format_flags, stdout, NULL);
+        } else
+                user_record_show(hr, true);
+}
+
+static char **mangle_user_list(char **list, char ***ret_allocated) {
+        _cleanup_free_ char *myself = NULL;
+        char **l;
+
+        if (!strv_isempty(list)) {
+                *ret_allocated = NULL;
+                return list;
+        }
+
+        myself = getusername_malloc();
+        if (!myself)
+                return NULL;
+
+        l = new(char*, 2);
+        if (!l)
+                return NULL;
+
+        l[0] = TAKE_PTR(myself);
+        l[1] = NULL;
+
+        *ret_allocated = l;
+        return l;
+}
+
+static int inspect_home(int argc, char *argv[], void *userdata) {
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        _cleanup_(strv_freep) char **mangled_list = NULL;
+        int r, ret = 0;
+        char **items, **i;
+
+        (void) pager_open(arg_pager_flags);
+
+        r = acquire_bus(&bus);
+        if (r < 0)
+                return r;
+
+        items = mangle_user_list(strv_skip(argv, 1), &mangled_list);
+        if (!items)
+                return log_oom();
+
+        STRV_FOREACH(i, items) {
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+                _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+                _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+                _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+                const char *json;
+                int incomplete;
+                uid_t uid;
+
+                r = parse_uid(*i, &uid);
+                if (r < 0) {
+                        if (!valid_user_group_name(*i)) {
+                                log_error("Invalid user name '%s'.", *i);
+                                if (ret == 0)
+                                        ret = -EINVAL;
+
+                                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);
+                }
+
+                if (r < 0) {
+                        log_error_errno(r, "Failed to inspect home: %s", bus_error_message(&error, r));
+                        if (ret == 0)
+                                ret = r;
+
+                        continue;
+                }
+
+                r = sd_bus_message_read(reply, "sbo", &json, &incomplete, NULL);
+                if (r < 0) {
+                        bus_log_parse_error(r);
+                        if (ret == 0)
+                                ret = r;
+
+                        continue;
+                }
+
+                r = json_parse(json, JSON_PARSE_SENSITIVE, &v, NULL, NULL);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to parse JSON identity: %m");
+                        if (ret == 0)
+                                ret = r;
+
+                        continue;
+                }
+
+                hr = user_record_new();
+                if (!hr)
+                        return log_oom();
+
+                r = user_record_load(hr, v, USER_RECORD_LOAD_REFUSE_SECRET|USER_RECORD_LOG);
+                if (r < 0) {
+                        if (ret == 0)
+                                ret = r;
+
+                        continue;
+                }
+
+                hr->incomplete = incomplete;
+                dump_home_record(hr);
+        }
+
+        return ret;
+}
+
+static int authenticate_home(int argc, char *argv[], void *userdata) {
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        _cleanup_(strv_freep) char **mangled_list = NULL;
+        int r, ret = 0;
+        char **i, **items;
+
+        items = mangle_user_list(strv_skip(argv, 1), &mangled_list);
+        if (!items)
+                return log_oom();
+
+        r = acquire_bus(&bus);
+        if (r < 0)
+                return r;
+
+        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
+
+        STRV_FOREACH(i, items) {
+                _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+
+                secret = user_record_new();
+                if (!secret)
+                        return log_oom();
+
+                for (;;) {
+                        _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");
+                        if (r < 0)
+                                return bus_log_create_error(r);
+
+                        r = sd_bus_message_append(m, "s", *i);
+                        if (r < 0)
+                                return bus_log_create_error(r);
+
+                        r = bus_message_append_secret(m, secret);
+                        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) {
+                                r = handle_generic_user_record_error(*i, secret, &error, r, false);
+                                if (r < 0) {
+                                        if (ret == 0)
+                                                ret = r;
+
+                                        break;
+                                }
+                        } else
+                                break;
+                }
+        }
+
+        return ret;
+}
+
+static int update_last_change(JsonVariant **v, bool with_password, bool override) {
+        JsonVariant *c;
+        usec_t n;
+        int r;
+
+        assert(v);
+
+        n = now(CLOCK_REALTIME);
+
+        c = json_variant_by_key(*v, "lastChangeUSec");
+        if (c) {
+                uintmax_t u;
+
+                if (!override)
+                        goto update_password;
+
+                if (!json_variant_is_unsigned(c))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "lastChangeUSec field is not an unsigned integer, refusing.");
+
+                u = json_variant_unsigned(c);
+                if (u >= n)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "lastChangeUSec is from the future, can't update.");
+        }
+
+        r = json_variant_set_field_unsigned(v, "lastChangeUSec", n);
+        if (r < 0)
+                return log_error_errno(r, "Failed to update lastChangeUSec: %m");
+
+update_password:
+        if (!with_password)
+                return 0;
+
+        c = json_variant_by_key(*v, "lastPasswordChangeUSec");
+        if (c) {
+                uintmax_t u;
+
+                if (!override)
+                        return 0;
+
+                if (!json_variant_is_unsigned(c))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "lastPasswordChangeUSec field is not an unsigned integer, refusing.");
+
+                u = json_variant_unsigned(c);
+                if (u >= n)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "lastPasswordChangeUSec is from the future, can't update.");
+        }
+
+        r = json_variant_set_field_unsigned(v, "lastPasswordChangeUSec", n);
+        if (r < 0)
+                return log_error_errno(r, "Failed to update lastPasswordChangeUSec: %m");
+
+        return 1;
+}
+
+static int apply_identity_changes(JsonVariant **_v) {
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        int r;
+
+        assert(_v);
+
+        v = json_variant_ref(*_v);
+
+        r = json_variant_filter(&v, arg_identity_filter);
+        if (r < 0)
+                return log_error_errno(r, "Failed to filter identity: %m");
+
+        r = json_variant_merge(&v, arg_identity_extra);
+        if (r < 0)
+                return log_error_errno(r, "Failed to merge identities: %m");
+
+        if (arg_identity_extra_this_machine || !strv_isempty(arg_identity_filter)) {
+                _cleanup_(json_variant_unrefp) JsonVariant *per_machine = NULL, *mmid = NULL;
+                char mids[SD_ID128_STRING_MAX];
+                sd_id128_t mid;
+
+                r = sd_id128_get_machine(&mid);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to acquire machine ID: %m");
+
+                r = json_variant_new_string(&mmid, sd_id128_to_string(mid, mids));
+                if (r < 0)
+                        return log_error_errno(r, "Failed to allocate matchMachineId object: %m");
+
+                per_machine = json_variant_ref(json_variant_by_key(v, "perMachine"));
+                if (per_machine) {
+                        _cleanup_(json_variant_unrefp) JsonVariant *npm = NULL, *add = NULL;
+                        _cleanup_free_ JsonVariant **array = NULL;
+                        JsonVariant *z;
+                        size_t i = 0;
+
+                        if (!json_variant_is_array(per_machine))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "perMachine field is not an array, refusing.");
+
+                        array = new(JsonVariant*, json_variant_elements(per_machine) + 1);
+                        if (!array)
+                                return log_oom();
+
+                        JSON_VARIANT_ARRAY_FOREACH(z, per_machine) {
+                                JsonVariant *u;
+
+                                if (!json_variant_is_object(z))
+                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "perMachine entry is not an object, refusing.");
+
+                                array[i++] = z;
+
+                                u = json_variant_by_key(z, "matchMachineId");
+                                if (!u)
+                                        continue;
+
+                                if (!json_variant_equal(u, mmid))
+                                        continue;
+
+                                r = json_variant_merge(&add, z);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to merge perMachine entry: %m");
+
+                                i--;
+                        }
+
+                        r = json_variant_filter(&add, arg_identity_filter);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to filter perMachine: %m");
+
+                        r = json_variant_merge(&add, arg_identity_extra_this_machine);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to merge in perMachine fields: %m");
+
+                        if (arg_identity_filter_rlimits || arg_identity_extra_rlimits) {
+                                _cleanup_(json_variant_unrefp) JsonVariant *rlv = NULL;
+
+                                rlv = json_variant_ref(json_variant_by_key(add, "resourceLimits"));
+
+                                r = json_variant_filter(&rlv, arg_identity_filter_rlimits);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to filter resource limits: %m");
+
+                                r = json_variant_merge(&rlv, arg_identity_extra_rlimits);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to set resource limits: %m");
+
+                                if (json_variant_is_blank_object(rlv)) {
+                                        r = json_variant_filter(&add, STRV_MAKE("resourceLimits"));
+                                        if (r < 0)
+                                                return log_error_errno(r, "Failed to drop resource limits field from identity: %m");
+                                } else {
+                                        r = json_variant_set_field(&add, "resourceLimits", rlv);
+                                        if (r < 0)
+                                                return log_error_errno(r, "Failed to update resource limits of identity: %m");
+                                }
+                        }
+
+                        if (!json_variant_is_blank_object(add)) {
+                                r = json_variant_set_field(&add, "matchMachineId", mmid);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to set matchMachineId field: %m");
+
+                                array[i++] = add;
+                        }
+
+                        r = json_variant_new_array(&npm, array, i);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to allocate new perMachine array: %m");
+
+                        json_variant_unref(per_machine);
+                        per_machine = TAKE_PTR(npm);
+                } else {
+                        _cleanup_(json_variant_unrefp) JsonVariant *item = json_variant_ref(arg_identity_extra_this_machine);
+
+                        if (arg_identity_extra_rlimits) {
+                                r = json_variant_set_field(&item, "resourceLimits", arg_identity_extra_rlimits);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to update resource limits of identity: %m");
+                        }
+
+                        r = json_variant_set_field(&item, "matchMachineId", mmid);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set matchMachineId field: %m");
+
+                        r = json_variant_append_array(&per_machine, item);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to append to perMachine array: %m");
+                }
+
+                r = json_variant_set_field(&v, "perMachine", per_machine);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to update per machine record: %m");
+        }
+
+        if (arg_identity_extra_privileged || arg_identity_filter) {
+                _cleanup_(json_variant_unrefp) JsonVariant *privileged = NULL;
+
+                privileged = json_variant_ref(json_variant_by_key(v, "privileged"));
+
+                r = json_variant_filter(&privileged, arg_identity_filter);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to filter identity (privileged part): %m");
+
+                r = json_variant_merge(&privileged, arg_identity_extra_privileged);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to merge identities (privileged part): %m");
+
+                if (json_variant_is_blank_object(privileged)) {
+                        r = json_variant_filter(&v, STRV_MAKE("privileged"));
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to drop privileged part from identity: %m");
+                } else {
+                        r = json_variant_set_field(&v, "privileged", privileged);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to update privileged part of identity: %m");
+                }
+        }
+
+        if (arg_identity_filter_rlimits) {
+                _cleanup_(json_variant_unrefp) JsonVariant *rlv = NULL;
+
+                rlv = json_variant_ref(json_variant_by_key(v, "resourceLimits"));
+
+                r = json_variant_filter(&rlv, arg_identity_filter_rlimits);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to filter resource limits: %m");
+
+                /* Note that we only filter resource limits here, but don't apply them. We do that in the perMachine section */
+
+                if (json_variant_is_blank_object(rlv)) {
+                        r = json_variant_filter(&v, STRV_MAKE("resourceLimits"));
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to drop resource limits field from identity: %m");
+                } else {
+                        r = json_variant_set_field(&v, "resourceLimits", rlv);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to update resource limits of identity: %m");
+                }
+        }
+
+        json_variant_unref(*_v);
+        *_v = TAKE_PTR(v);
+
+        return 0;
+}
+
+static int add_disposition(JsonVariant **v) {
+        int r;
+
+        assert(v);
+
+        if (json_variant_by_key(*v, "disposition"))
+                return 0;
+
+        /* Set the disposition to regular, if not configured explicitly */
+        r = json_variant_set_field_string(v, "disposition", "regular");
+        if (r < 0)
+                return log_error_errno(r, "Failed to set disposition field: %m");
+
+        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 exract 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;
+        char **i;
+        int r;
+
+        assert(ret);
+
+        if (arg_identity) {
+                unsigned line, column;
+
+                r = json_parse_file(
+                                streq(arg_identity, "-") ? stdin : NULL,
+                                streq(arg_identity, "-") ? "<stdin>" : arg_identity, JSON_PARSE_SENSITIVE, &v, &line, &column);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column);
+        }
+
+        r = apply_identity_changes(&v);
+        if (r < 0)
+                return r;
+
+        r = add_disposition(&v);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(i, arg_pkcs11_token_uri) {
+                r = add_pkcs11_key_data(&v, *i);
+                if (r < 0)
+                        return r;
+        }
+
+        r = update_last_change(&v, true, false);
+        if (r < 0)
+                return r;
+
+        if (DEBUG_LOGGING)
+                json_variant_dump(v, JSON_FORMAT_PRETTY, NULL, NULL);
+
+        hr = user_record_new();
+        if (!hr)
+                return log_oom();
+
+        r = user_record_load(hr, v, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_LOG);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(hr);
+        return 0;
+}
+
+static int acquire_new_password(
+                const char *user_name,
+                UserRecord *hr,
+                bool suggest) {
+
+        unsigned i = 5;
+        char *e;
+        int r;
+
+        assert(user_name);
+        assert(hr);
+
+        e = getenv("NEWPASSWORD");
+        if (e) {
+                /* As above, this is not for use, just for testing */
+
+                r = user_record_set_password(hr, STRV_MAKE(e), /* prepend = */ false);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to store password: %m");
+
+                string_erase(e);
+
+                if (unsetenv("NEWPASSWORD") < 0)
+                        return log_error_errno(errno, "Failed to unse $NEWPASSWORD: %m");
+
+                return 0;
+        }
+
+        if (suggest)
+                (void) suggest_passwords();
+
+        for (;;) {
+                _cleanup_(strv_free_erasep) char **first = NULL, **second = NULL;
+                _cleanup_free_ char *question = NULL;
+
+                if (--i == 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Too many attempts, giving up:");
+
+                if (asprintf(&question, "Please enter new password for user %s:", user_name) < 0)
+                        return log_oom();
+
+                r = ask_password_auto(question, "user-home", NULL, "home-password", USEC_INFINITY, 0, &first);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to acquire password: %m");
+
+                question = mfree(question);
+                if (asprintf(&question, "Please enter new password for user %s (repeat):", user_name) < 0)
+                        return log_oom();
+
+                r = ask_password_auto(question, "user-home", NULL, "home-password", USEC_INFINITY, 0, &second);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to acquire password: %m");
+
+                if (strv_equal(first, second)) {
+                        r = user_record_set_password(hr, first, /* prepend = */ false);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to store password: %m");
+
+                        return 0;
+                }
+
+                log_error("Password didn't mach, try again.");
+        }
+}
+
+static int create_home(int argc, char *argv[], void *userdata) {
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+        _cleanup_strv_free_ char **original_hashed_passwords = NULL;
+        int r;
+
+        r = acquire_bus(&bus);
+        if (r < 0)
+                return r;
+
+        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
+
+        if (argc >= 2) {
+                /* If a username was specified, use it */
+
+                if (valid_user_group_name(argv[1]))
+                        r = json_variant_set_field_string(&arg_identity_extra, "userName", argv[1]);
+                else {
+                        _cleanup_free_ char *un = NULL, *rr = NULL;
+
+                        /* Before we consider the user name invalid, let's check if we can split it? */
+                        r = split_user_name_realm(argv[1], &un, &rr);
+                        if (r < 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name '%s' is not valid: %m", argv[1]);
+
+                        if (rr) {
+                                r = json_variant_set_field_string(&arg_identity_extra, "realm", rr);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to set realm field: %m");
+                        }
+
+                        r = json_variant_set_field_string(&arg_identity_extra, "userName", un);
+                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set userName field: %m");
+        } else {
+                /* If neither a username nor an identity have been specified we cannot operate. */
+                if (!arg_identity)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name required.");
+        }
+
+        r = acquire_new_home_record(&hr);
+        if (r < 0)
+                return r;
+
+        /* Remember the original hashed paswords 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)
+                return log_oom();
+
+        /* If the JSON record carries no plain text password, then let's query it manually. */
+        if (!hr->password) {
+
+                if (strv_isempty(hr->hashed_password)) {
+                        /* No regular (i.e. non-PKCS#11) hashed passwords set in the record, let's fix that. */
+                        r = acquire_new_password(hr->user_name, hr, /* suggest = */ true);
+                        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 password: %m");
+                } else {
+                        /* There's a hash password set in the record, acquire the unhashed version of it. */
+                        r = acquire_existing_password(hr->user_name, hr, false);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        if (hr->enforce_password_policy == 0) {
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+
+                /* If password quality enforcement is disabled, let's at least warn client side */
+
+                r = quality_check_password(hr, hr, &error);
+                if (r < 0)
+                        log_warning_errno(r, "Specified password does not pass quality checks (%s), proceeding anyway.", bus_error_message(&error, r));
+        }
+
+        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_(erase_and_freep) char *formatted = NULL;
+
+                r = json_variant_format(hr->json, 0, &formatted);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_new_method_call(
+                                bus,
+                                &m,
+                                "org.freedesktop.home1",
+                                "/org/freedesktop/home1",
+                                "org.freedesktop.home1.Manager",
+                                "CreateHome");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                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 */
+
+                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 = user_record_make_hashed_password(hr, hr->password, /* extend = */ true);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to hash passwords: %m");
+        }
+
+        return 0;
+}
+
+static int remove_home(int argc, char *argv[], void *userdata) {
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        int r, ret = 0;
+        char **i;
+
+        r = acquire_bus(&bus);
+        if (r < 0)
+                return r;
+
+        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
+
+        STRV_FOREACH(i, strv_skip(argv, 1)) {
+                _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");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append(m, "s", *i);
+                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) {
+                        log_error_errno(r, "Failed to remove home: %s", bus_error_message(&error, r));
+                        if (ret == 0)
+                                ret = r;
+                }
+        }
+
+        return ret;
+}
+
+static int acquire_updated_home_record(
+                sd_bus *bus,
+                const char *username,
+                UserRecord **ret) {
+
+        _cleanup_(json_variant_unrefp) JsonVariant *json = NULL;
+        _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+        char **i;
+        int r;
+
+        assert(ret);
+
+        if (arg_identity) {
+                unsigned line, column;
+                JsonVariant *un;
+
+                r = json_parse_file(
+                                streq(arg_identity, "-") ? stdin : NULL,
+                                streq(arg_identity, "-") ? "<stdin>" : arg_identity, JSON_PARSE_SENSITIVE, &json, &line, &column);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column);
+
+                un = json_variant_by_key(json, "userName");
+                if (un) {
+                        if (!json_variant_is_string(un) || (username && !streq(json_variant_string(un), username)))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name specified on command line and in JSON record do not match.");
+                } else {
+                        if (!username)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No username specified.");
+
+                        r = json_variant_set_field_string(&arg_identity_extra, "userName", username);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set userName field: %m");
+                }
+
+        } else {
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+                _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+                int incomplete;
+                const char *text;
+
+                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);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to acquire user home record: %s", bus_error_message(&error, r));
+
+                r = sd_bus_message_read(reply, "sbo", &text, &incomplete, NULL);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                if (incomplete)
+                        return log_error_errno(SYNTHETIC_ERRNO(EACCES), "Lacking rights to acquire user record including privileged metadata, can't update record.");
+
+                r = json_parse(text, JSON_PARSE_SENSITIVE, &json, NULL, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse JSON identity: %m");
+
+                reply = sd_bus_message_unref(reply);
+
+                r = json_variant_filter(&json, STRV_MAKE("binding", "status", "signature"));
+                if (r < 0)
+                        return log_error_errno(r, "Failed to strip binding and status from record to update: %m");
+        }
+
+        r = apply_identity_changes(&json);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(i, arg_pkcs11_token_uri) {
+                r = add_pkcs11_key_data(&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);
+        if (r < 0)
+                return r;
+
+        if (DEBUG_LOGGING)
+                json_variant_dump(json, JSON_FORMAT_PRETTY, NULL, NULL);
+
+        hr = user_record_new();
+        if (!hr)
+                return log_oom();
+
+        r = user_record_load(hr, json, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_LOG);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(hr);
+        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;
+        _cleanup_free_ char *buffer = NULL;
+        const char *username;
+        int r;
+
+        if (argc >= 2)
+                username = argv[1];
+        else if (!arg_identity) {
+                buffer = getusername_malloc();
+                if (!buffer)
+                        return log_oom();
+
+                username = buffer;
+        } else
+                username = NULL;
+
+        r = acquire_bus(&bus);
+        if (r < 0)
+                return r;
+
+        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
+
+        r = acquire_updated_home_record(bus, username, &hr);
+        if (r < 0)
+                return r;
+
+        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");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = json_variant_format(hr->json, 0, &formatted);
+                if (r < 0)
+                        return r;
+
+                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 (arg_and_change_password &&
+                            sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
+                                /* In the generic handler we'd ask for a password in this case, but when
+                                 * changing passwords that's not sufficient, as we need to acquire all keys
+                                 * first. */
+                                return log_error_errno(r, "Security token not inserted, refusing.");
+
+                        r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false);
+                        if (r < 0)
+                                return r;
+                } else
+                        break;
+        }
+
+        /* 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");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                /* Specify UINT64_MAX as size, in which case the underlying disk size will just be synced */
+                r = sd_bus_message_append(m, "st", hr->user_name, UINT64_MAX);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = bus_message_append_secret(m, hr);
+                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 (arg_and_change_password &&
+                            sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
+                                return log_error_errno(r, "Security token not inserted, refusing.");
+
+                        r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false);
+                        if (r < 0)
+                                return r;
+                } else
+                        break;
+        }
+
+        /* 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");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                /* Specify an empty new secret, in which case the underlying LUKS/fscrypt password will just be synced */
+                r = sd_bus_message_append(m, "ss", hr->user_name, "{}");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = bus_message_append_secret(m, hr);
+                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_BAD_PASSWORD_AND_NO_TOKEN))
+                                return log_error_errno(r, "Security token not inserted, refusing.");
+
+                        r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false);
+                        if (r < 0)
+                                return r;
+                } else
+                        break;
+        }
+
+        return 0;
+}
+
+static int passwd_home(int argc, char *argv[], void *userdata) {
+        _cleanup_(user_record_unrefp) UserRecord *old_secret = NULL, *new_secret = NULL;
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        _cleanup_free_ char *buffer = NULL;
+        const char *username;
+        int r;
+
+        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 (identity_properties_specified())
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The 'passwd' verb does not permit changing other record properties at the same time.");
+
+        if (argc >= 2)
+                username = argv[1];
+        else {
+                buffer = getusername_malloc();
+                if (!buffer)
+                        return log_oom();
+
+                username = buffer;
+        }
+
+        r = acquire_bus(&bus);
+        if (r < 0)
+                return r;
+
+        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
+
+        old_secret = user_record_new();
+        if (!old_secret)
+                return log_oom();
+
+        new_secret = user_record_new();
+        if (!new_secret)
+                return log_oom();
+
+        r = acquire_new_password(username, new_secret, /* suggest = */ true);
+        if (r < 0)
+                return r;
+
+        for (;;) {
+                _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");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append(m, "s", username);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = bus_message_append_secret(m, new_secret);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = bus_message_append_secret(m, old_secret);
+                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)) {
+
+                                log_error_errno(r, "%s", bus_error_message(&error, r));
+
+                                r = acquire_new_password(username, new_secret, /* suggest = */ false);
+
+                        } else if (sd_bus_error_has_name(&error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN))
+
+                                /* In the generic handler we'd ask for a password in this case, but when
+                                 * changing passwords that's not sufficeint, as we need to acquire all keys
+                                 * first. */
+                                return log_error_errno(r, "Security token not inserted, refusing.");
+                        else
+                                r = handle_generic_user_record_error(username, old_secret, &error, r, true);
+                        if (r < 0)
+                                return r;
+                } else
+                        break;
+        }
+
+        return 0;
+}
+
+static int resize_home(int argc, char *argv[], void *userdata) {
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+        uint64_t ds = UINT64_MAX;
+        int r;
+
+        r = acquire_bus(&bus);
+        if (r < 0)
+                return r;
+
+        (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
+
+        if (arg_disk_size_relative != UINT64_MAX ||
+            (argc > 2 && parse_percent(argv[2]) >= 0))
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                               "Relative disk size specification currently not supported when resizing.");
+
+        if (argc > 2) {
+                r = parse_size(argv[2], 1024, &ds);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse disk size parameter: %s", argv[2]);
+        }
+
+        if (arg_disk_size != UINT64_MAX) {
+                if (ds != UINT64_MAX && ds != arg_disk_size)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Disk size specified twice and doesn't match, refusing.");
+
+                ds = arg_disk_size;
+        }
+
+        secret = user_record_new();
+        if (!secret)
+                return log_oom();
+
+        for (;;) {
+                _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");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append(m, "st", argv[1], ds);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = bus_message_append_secret(m, secret);
+                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) {
+                        r = handle_generic_user_record_error(argv[1], secret, &error, r, false);
+                        if (r < 0)
+                                return r;
+                } else
+                        break;
+        }
+
+        return 0;
+}
+
+static int lock_home(int argc, char *argv[], void *userdata) {
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        int r, ret = 0;
+        char **i;
+
+        r = acquire_bus(&bus);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(i, strv_skip(argv, 1)) {
+                _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");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append(m, "s", *i);
+                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) {
+                        log_error_errno(r, "Failed to lock home: %s", bus_error_message(&error, r));
+                        if (ret == 0)
+                                ret = r;
+                }
+        }
+
+        return ret;
+}
+
+static int unlock_home(int argc, char *argv[], void *userdata) {
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        int r, ret = 0;
+        char **i;
+
+        r = acquire_bus(&bus);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(i, strv_skip(argv, 1)) {
+                _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+
+                secret = user_record_new();
+                if (!secret)
+                        return log_oom();
+
+                for (;;) {
+                        _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");
+                        if (r < 0)
+                                return bus_log_create_error(r);
+
+                        r = sd_bus_message_append(m, "s", *i);
+                        if (r < 0)
+                                return bus_log_create_error(r);
+
+                        r = bus_message_append_secret(m, secret);
+                        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) {
+                                r = handle_generic_user_record_error(argv[1], secret, &error, r, false);
+                                if (r < 0) {
+                                        if (ret == 0)
+                                                ret = r;
+
+                                        break;
+                                }
+                        } else
+                                break;
+                }
+        }
+
+        return ret;
+}
+
+static int with_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, *reply = NULL;
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+        _cleanup_close_ int acquired_fd = -1;
+        _cleanup_strv_free_ char **cmdline  = NULL;
+        const char *home;
+        int r, ret;
+        pid_t pid;
+
+        r = acquire_bus(&bus);
+        if (r < 0)
+                return r;
+
+        if (argc < 3) {
+                _cleanup_free_ char *shell = NULL;
+
+                /* If no command is specified, spawn a shell */
+                r = get_shell(&shell);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to acquire shell: %m");
+
+                cmdline = strv_new(shell);
+        } else
+                cmdline = strv_copy(argv + 2);
+        if (!cmdline)
+                return log_oom();
+
+        secret = user_record_new();
+        if (!secret)
+                return log_oom();
+
+        for (;;) {
+                r = sd_bus_message_new_method_call(
+                                bus,
+                                &m,
+                                "org.freedesktop.home1",
+                                "/org/freedesktop/home1",
+                                "org.freedesktop.home1.Manager",
+                                "AcquireHome");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append(m, "s", argv[1]);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = bus_message_append_secret(m, secret);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append(m, "b", /* please_suspend = */ getenv_bool("SYSTEMD_PLEASE_SUSPEND_HOME") > 0);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
+                m = sd_bus_message_unref(m);
+                if (r < 0) {
+                        r = handle_generic_user_record_error(argv[1], secret, &error, r, false);
+                        if (r < 0)
+                                return r;
+
+                        sd_bus_error_free(&error);
+                } else {
+                        int fd;
+
+                        r = sd_bus_message_read(reply, "h", &fd);
+                        if (r < 0)
+                                return bus_log_parse_error(r);
+
+                        acquired_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+                        if (acquired_fd < 0)
+                                return log_error_errno(errno, "Failed to duplicate acquired fd: %m");
+
+                        reply = sd_bus_message_unref(reply);
+                        break;
+                }
+        }
+
+        r = sd_bus_call_method(
+                        bus,
+                        "org.freedesktop.home1",
+                        "/org/freedesktop/home1",
+                        "org.freedesktop.home1.Manager",
+                        "GetHomeByName",
+                        &error,
+                        &reply,
+                        "s",
+                        argv[1]);
+        if (r < 0)
+                return log_error_errno(r, "Failed to inspect home: %s", bus_error_message(&error, r));
+
+        r = sd_bus_message_read(reply, "usussso", NULL, NULL, NULL, NULL, &home, NULL, NULL);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        r = safe_fork("(with)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE|FORK_REOPEN_LOG, &pid);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                if (chdir(home) < 0) {
+                        log_error_errno(errno, "Failed to change to directory %s: %m", home);
+                        _exit(255);
+                }
+
+                execvp(cmdline[0], cmdline);
+                log_error_errno(errno, "Failed to execute %s: %m", cmdline[0]);
+                _exit(255);
+        }
+
+        ret = wait_for_terminate_and_check(cmdline[0], pid, WAIT_LOG_ABNORMAL);
+
+        /* 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");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append(m, "s", argv[1]);
+        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_HOME_BUSY))
+                        log_notice("Not deactivating home directory of %s, as it is still used.", argv[1]);
+                else
+                        return log_error_errno(r, "Failed to release user home: %s", bus_error_message(&error, r));
+        }
+
+        return ret;
+}
+
+static int lock_all_homes(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;
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        int r;
+
+        r = acquire_bus(&bus);
+        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");
+        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)
+                return log_error_errno(r, "Failed to lock home: %s", bus_error_message(&error, r));
+
+        return 0;
+}
+
+static int drop_from_identity(const char *field) {
+        int r;
+
+        assert(field);
+
+        /* If we are called to update an identity record and drop some field, let's keep track of what to
+         * remove from the old record */
+        r = strv_extend(&arg_identity_filter, field);
+        if (r < 0)
+                return log_oom();
+
+        /* Let's also drop the field if it was previously set to a new value on the same command line */
+        r = json_variant_filter(&arg_identity_extra, STRV_MAKE(field));
+        if (r < 0)
+                return log_error_errno(r, "Failed to filter JSON identity data: %m");
+
+        r = json_variant_filter(&arg_identity_extra_this_machine, STRV_MAKE(field));
+        if (r < 0)
+                return log_error_errno(r, "Failed to filter JSON identity data: %m");
+
+        r = json_variant_filter(&arg_identity_extra_privileged, STRV_MAKE(field));
+        if (r < 0)
+                return log_error_errno(r, "Failed to filter JSON identity data: %m");
+
+        return 0;
+}
+
+static int help(int argc, char *argv[], void *userdata) {
+        _cleanup_free_ char *link = NULL;
+        int r;
+
+        (void) pager_open(arg_pager_flags);
+
+        r = terminal_urlify_man("homectl", "1", &link);
+        if (r < 0)
+                return log_oom();
+
+        printf("%1$s [OPTIONS...] COMMAND ...\n\n"
+               "%2$sCreate, manipulate or inspect home directories.%3$s\n"
+               "\n%4$sCommands:%5$s\n"
+               "  list                        List homes\n"
+               "  activate USER…              Activate home\n"
+               "  deactivate USER…            Deactivate home\n"
+               "  inspect USER…               Inspect home\n"
+               "  authenticate USER…          Authenticate home\n"
+               "  create USER                 Create a home area\n"
+               "  remove USER…                Remove a home area\n"
+               "  update USER                 Update a home area\n"
+               "  passwd USER                 Change password of a home area\n"
+               "  resize USER SIZE            Resize a home area\n"
+               "  lock USER…                  Temporarily lock an active home\n"
+               "  unlock USER…                Unlock a temporarily locked home\n"
+               "  lock-all                    Lock all suitable homes\n"
+               "  with USER [COMMAND…]        Run shell or command with access to home\n"
+               "\n%4$sOptions:%5$s\n"
+               "  -h --help                   Show this help\n"
+               "     --version                Show package version\n"
+               "     --no-pager               Do not pipe output into a pager\n"
+               "     --no-legend              Do not show the headers and footers\n"
+               "     --no-ask-password        Do not ask for system passwords\n"
+               "  -H --host=[USER@]HOST       Operate on remote host\n"
+               "  -M --machine=CONTAINER      Operate on local container\n"
+               "     --identity=PATH          Read JSON identity from file\n"
+               "     --json=FORMAT            Output inspection data in JSON (takes one of\n"
+               "                              pretty, short, off)\n"
+               "  -j                          Equivalent to --json=pretty (on TTY) or\n"
+               "                              --json=short (otherwise)\n"
+               "     --export-format=         Strip JSON inspection data (full, stripped,\n"
+               "                              minimal)\n"
+               "  -E                          When specified once equals -j --export-format=\n"
+               "                              stripped, when specified twice equals\n"
+               "                              -j --export-format=minimal\n"
+               "\n%4$sGeneral User Record Properties:%5$s\n"
+               "  -c --real-name=REALNAME     Real name for user\n"
+               "     --realm=REALM            Realm to create user in\n"
+               "     --email-address=EMAIL    Email address for user\n"
+               "     --location=LOCATION      Set location of user on earth\n"
+               "     --icon-name=NAME         Icon name for user\n"
+               "  -d --home-dir=PATH          Home directory\n"
+               "     --uid=UID                Numeric UID for user\n"
+               "  -G --member-of=GROUP        Add user to group\n"
+               "     --skel=PATH              Skeleton directory to use\n"
+               "     --shell=PATH             Shell for account\n"
+               "     --setenv=VARIABLE=VALUE  Set an environment variable at log-in\n"
+               "     --timezone=TIMEZONE      Set a time-zone\n"
+               "     --language=LOCALE        Set preferred language\n"
+               "     --ssh-authorized-keys=KEYS\n"
+               "                              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"
+               "\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"
+               "     --not-after=TIMESTAMP    Do not allow logins after\n"
+               "     --rate-limit-interval=SECS\n"
+               "                              Login rate-limit interval in seconds\n"
+               "     --rate-limit-burst=NUMBER\n"
+               "                              Login rate-limit attempts per interval\n"
+               "\n%4$sPassword Policy User Record Properties:%5$s\n"
+               "     --password-hint=HINT     Set Password hint\n"
+               "     --enforce-password-policy=BOOL\n"
+               "                              Control whether to enforce system's password\n"
+               "                              policy for this user\n"
+               "  -P                          Equivalent to --enforce-password-password=no\n"
+               "     --password-change-now=BOOL\n"
+               "                              Require the password to be changed on next login\n"
+               "     --password-change-min=TIME\n"
+               "                              Require minimum time between password changes\n"
+               "     --password-change-max=TIME\n"
+               "                              Require maximum time between password changes\n"
+               "     --password-change-warn=TIME\n"
+               "                              How much time to warn before password expiry\n"
+               "     --password-change-inactive=TIME\n"
+               "                              How much time to block password after expiry\n"
+               "\n%4$sResource Management User Record Properties:%5$s\n"
+               "     --disk-size=BYTES        Size to assign the user on disk\n"
+               "     --access-mode=MODE       User home directory access mode\n"
+               "     --umask=MODE             Umask for user when logging in\n"
+               "     --nice=NICE              Nice level for user\n"
+               "     --rlimit=LIMIT=VALUE[:VALUE]\n"
+               "                              Set resource limits\n"
+               "     --tasks-max=MAX          Set maximum number of per-user tasks\n"
+               "     --memory-high=BYTES      Set high memory threshold in bytes\n"
+               "     --memory-max=BYTES       Set maximum memory limit\n"
+               "     --cpu-weight=WEIGHT      Set CPU weight\n"
+               "     --io-weight=WEIGHT       Set IO weight\n"
+               "\n%4$sStorage User Record Properties:%5$s\n"
+               "     --storage=STORAGE        Storage type to use (luks, fscrypt, directory,\n"
+               "                              subvolume, cifs)\n"
+               "     --image-path=PATH        Path to image file/directory\n"
+               "\n%4$sLUKS Storage User Record Properties:%5$s\n"
+               "     --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"
+               "     --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"
+               "                              Volume key size to use for LUKS encryption\n"
+               "     --luks-pbkdf-type=TYPE   Password-based Key Derivation Function to use\n"
+               "     --luks-pbkdf-hash-algorithm=ALGORITHM\n"
+               "                              PBKDF hash algorithm to use\n"
+               "     --luks-pbkdf-time-cost=SECS\n"
+               "                              Time cost for PBKDF in seconds\n"
+               "     --luks-pbkdf-memory-cost=BYTES\n"
+               "                              Memory cost for PBKDF in bytes\n"
+               "     --luks-pbkdf-parallel-threads=NUMBER\n"
+               "                              Number of parallel threads for PKBDF\n"
+               "\n%4$sMounting User Record Properties:%5$s\n"
+               "     --nosuid=BOOL            Control the 'nosuid' flag of the home mount\n"
+               "     --nodev=BOOL             Control the 'nodev' flag of the home mount\n"
+               "     --noexec=BOOL            Control the 'noexec' flag of the home mount\n"
+               "\n%4$sCIFS User Record Properties:%5$s\n"
+               "     --cifs-domain=DOMAIN     CIFS (Windows) domain\n"
+               "     --cifs-user-name=USER    CIFS (Windows) user name\n"
+               "     --cifs-service=SERVICE   CIFS (Windows) service to mount as home\n"
+               "\n%4$sLogin Behaviour User Record Properties:%5$s\n"
+               "     --stop-delay=SECS        How long to leave user services running after\n"
+               "                              logout\n"
+               "     --kill-processes=BOOL    Whether to kill user processes when sessions\n"
+               "                              terminate\n"
+               "     --auto-login=BOOL        Try to log this user in automatically\n"
+               "\nSee the %6$s for details.\n"
+               , program_invocation_short_name
+               , ansi_highlight(), ansi_normal()
+               , ansi_underline(), ansi_normal()
+               , link
+        );
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_VERSION = 0x100,
+                ARG_NO_PAGER,
+                ARG_NO_LEGEND,
+                ARG_NO_ASK_PASSWORD,
+                ARG_REALM,
+                ARG_EMAIL_ADDRESS,
+                ARG_DISK_SIZE,
+                ARG_ACCESS_MODE,
+                ARG_STORAGE,
+                ARG_FS_TYPE,
+                ARG_IMAGE_PATH,
+                ARG_UMASK,
+                ARG_LUKS_DISCARD,
+                ARG_JSON,
+                ARG_SETENV,
+                ARG_TIMEZONE,
+                ARG_LANGUAGE,
+                ARG_LOCKED,
+                ARG_SSH_AUTHORIZED_KEYS,
+                ARG_LOCATION,
+                ARG_ICON_NAME,
+                ARG_PASSWORD_HINT,
+                ARG_NICE,
+                ARG_RLIMIT,
+                ARG_NOT_BEFORE,
+                ARG_NOT_AFTER,
+                ARG_LUKS_CIPHER,
+                ARG_LUKS_CIPHER_MODE,
+                ARG_LUKS_VOLUME_KEY_SIZE,
+                ARG_NOSUID,
+                ARG_NODEV,
+                ARG_NOEXEC,
+                ARG_CIFS_DOMAIN,
+                ARG_CIFS_USER_NAME,
+                ARG_CIFS_SERVICE,
+                ARG_TASKS_MAX,
+                ARG_MEMORY_HIGH,
+                ARG_MEMORY_MAX,
+                ARG_CPU_WEIGHT,
+                ARG_IO_WEIGHT,
+                ARG_LUKS_PBKDF_TYPE,
+                ARG_LUKS_PBKDF_HASH_ALGORITHM,
+                ARG_LUKS_PBKDF_TIME_COST,
+                ARG_LUKS_PBKDF_MEMORY_COST,
+                ARG_LUKS_PBKDF_PARALLEL_THREADS,
+                ARG_RATE_LIMIT_INTERVAL,
+                ARG_RATE_LIMIT_BURST,
+                ARG_STOP_DELAY,
+                ARG_KILL_PROCESSES,
+                ARG_ENFORCE_PASSWORD_POLICY,
+                ARG_PASSWORD_CHANGE_NOW,
+                ARG_PASSWORD_CHANGE_MIN,
+                ARG_PASSWORD_CHANGE_MAX,
+                ARG_PASSWORD_CHANGE_WARN,
+                ARG_PASSWORD_CHANGE_INACTIVE,
+                ARG_EXPORT_FORMAT,
+                ARG_AUTO_LOGIN,
+                ARG_PKCS11_TOKEN_URI,
+                ARG_AND_RESIZE,
+                ARG_AND_CHANGE_PASSWORD,
+        };
+
+        static const struct option options[] = {
+                { "help",                        no_argument,       NULL, 'h'                             },
+                { "version",                     no_argument,       NULL, ARG_VERSION                     },
+                { "no-pager",                    no_argument,       NULL, ARG_NO_PAGER                    },
+                { "no-legend",                   no_argument,       NULL, ARG_NO_LEGEND                   },
+                { "no-ask-password",             no_argument,       NULL, ARG_NO_ASK_PASSWORD             },
+                { "host",                        required_argument, NULL, 'H'                             },
+                { "machine",                     required_argument, NULL, 'M'                             },
+                { "identity",                    required_argument, NULL, 'I'                             },
+                { "real-name",                   required_argument, NULL, 'c'                             },
+                { "comment",                     required_argument, NULL, 'c'                             }, /* Compat alias to keep thing in sync with useradd(8) */
+                { "realm",                       required_argument, NULL, ARG_REALM                       },
+                { "email-address",               required_argument, NULL, ARG_EMAIL_ADDRESS               },
+                { "location",                    required_argument, NULL, ARG_LOCATION                    },
+                { "password-hint",               required_argument, NULL, ARG_PASSWORD_HINT               },
+                { "icon-name",                   required_argument, NULL, ARG_ICON_NAME                   },
+                { "home-dir",                    required_argument, NULL, 'd'                             }, /* Compatible with useradd(8) */
+                { "uid",                         required_argument, NULL, 'u'                             }, /* Compatible with useradd(8) */
+                { "member-of",                   required_argument, NULL, 'G'                             },
+                { "groups",                      required_argument, NULL, 'G'                             }, /* Compat alias to keep thing in sync with useradd(8) */
+                { "skel",                        required_argument, NULL, 'k'                             }, /* Compatible with useradd(8) */
+                { "shell",                       required_argument, NULL, 's'                             }, /* Compatible with useradd(8) */
+                { "setenv",                      required_argument, NULL, ARG_SETENV                      },
+                { "timezone",                    required_argument, NULL, ARG_TIMEZONE                    },
+                { "language",                    required_argument, NULL, ARG_LANGUAGE                    },
+                { "locked",                      required_argument, NULL, ARG_LOCKED                      },
+                { "not-before",                  required_argument, NULL, ARG_NOT_BEFORE                  },
+                { "not-after",                   required_argument, NULL, ARG_NOT_AFTER                   },
+                { "expiredate",                  required_argument, NULL, 'e'                             }, /* Compat alias to keep thing in sync with useradd(8) */
+                { "ssh-authorized-keys",         required_argument, NULL, ARG_SSH_AUTHORIZED_KEYS         },
+                { "disk-size",                   required_argument, NULL, ARG_DISK_SIZE                   },
+                { "access-mode",                 required_argument, NULL, ARG_ACCESS_MODE                 },
+                { "umask",                       required_argument, NULL, ARG_UMASK                       },
+                { "nice",                        required_argument, NULL, ARG_NICE                        },
+                { "rlimit",                      required_argument, NULL, ARG_RLIMIT                      },
+                { "tasks-max",                   required_argument, NULL, ARG_TASKS_MAX                   },
+                { "memory-high",                 required_argument, NULL, ARG_MEMORY_HIGH                 },
+                { "memory-max",                  required_argument, NULL, ARG_MEMORY_MAX                  },
+                { "cpu-weight",                  required_argument, NULL, ARG_CPU_WEIGHT                  },
+                { "io-weight",                   required_argument, NULL, ARG_IO_WEIGHT                   },
+                { "storage",                     required_argument, NULL, ARG_STORAGE                     },
+                { "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-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        },
+                { "luks-pbkdf-type",             required_argument, NULL, ARG_LUKS_PBKDF_TYPE             },
+                { "luks-pbkdf-hash-algorithm",   required_argument, NULL, ARG_LUKS_PBKDF_HASH_ALGORITHM   },
+                { "luks-pbkdf-time-cost",        required_argument, NULL, ARG_LUKS_PBKDF_TIME_COST        },
+                { "luks-pbkdf-memory-cost",      required_argument, NULL, ARG_LUKS_PBKDF_MEMORY_COST      },
+                { "luks-pbkdf-parallel-threads", required_argument, NULL, ARG_LUKS_PBKDF_PARALLEL_THREADS },
+                { "nosuid",                      required_argument, NULL, ARG_NOSUID                      },
+                { "nodev",                       required_argument, NULL, ARG_NODEV                       },
+                { "noexec",                      required_argument, NULL, ARG_NOEXEC                      },
+                { "cifs-user-name",              required_argument, NULL, ARG_CIFS_USER_NAME              },
+                { "cifs-domain",                 required_argument, NULL, ARG_CIFS_DOMAIN                 },
+                { "cifs-service",                required_argument, NULL, ARG_CIFS_SERVICE                },
+                { "rate-limit-interval",         required_argument, NULL, ARG_RATE_LIMIT_INTERVAL         },
+                { "rate-limit-burst",            required_argument, NULL, ARG_RATE_LIMIT_BURST            },
+                { "stop-delay",                  required_argument, NULL, ARG_STOP_DELAY                  },
+                { "kill-processes",              required_argument, NULL, ARG_KILL_PROCESSES              },
+                { "enforce-password-policy",     required_argument, NULL, ARG_ENFORCE_PASSWORD_POLICY     },
+                { "password-change-now",         required_argument, NULL, ARG_PASSWORD_CHANGE_NOW         },
+                { "password-change-min",         required_argument, NULL, ARG_PASSWORD_CHANGE_MIN         },
+                { "password-change-max",         required_argument, NULL, ARG_PASSWORD_CHANGE_MAX         },
+                { "password-change-warn",        required_argument, NULL, ARG_PASSWORD_CHANGE_WARN        },
+                { "password-change-inactive",    required_argument, NULL, ARG_PASSWORD_CHANGE_INACTIVE    },
+                { "auto-login",                  required_argument, NULL, ARG_AUTO_LOGIN                  },
+                { "json",                        required_argument, NULL, ARG_JSON                        },
+                { "export-format",               required_argument, NULL, ARG_EXPORT_FORMAT               },
+                { "pkcs11-token-uri",            required_argument, NULL, ARG_PKCS11_TOKEN_URI            },
+                { "and-resize",                  required_argument, NULL, ARG_AND_RESIZE                  },
+                { "and-change-password",         required_argument, NULL, ARG_AND_CHANGE_PASSWORD         },
+                {}
+        };
+
+        int r;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        for (;;) {
+                int c;
+
+                c = getopt_long(argc, argv, "hH:M:I:c:d:u:k:s:e:G:jPE", options, NULL);
+                if (c < 0)
+                        break;
+
+                switch (c) {
+
+                case 'h':
+                        return help(0, NULL, NULL);
+
+                case ARG_VERSION:
+                        return version();
+
+                case ARG_NO_PAGER:
+                        arg_pager_flags |= PAGER_DISABLE;
+                        break;
+
+                case ARG_NO_LEGEND:
+                        arg_legend = false;
+                        break;
+
+                case ARG_NO_ASK_PASSWORD:
+                        arg_ask_password = false;
+                        break;
+
+                case 'H':
+                        arg_transport = BUS_TRANSPORT_REMOTE;
+                        arg_host = optarg;
+                        break;
+
+                case 'M':
+                        arg_transport = BUS_TRANSPORT_MACHINE;
+                        arg_host = optarg;
+                        break;
+
+                case 'I':
+                        arg_identity = optarg;
+                        break;
+
+                case 'c':
+                        if (isempty(optarg)) {
+                                r = drop_from_identity("realName");
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        if (!valid_gecos(optarg))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Real name '%s' not a valid GECOS field.", optarg);
+
+                        r = json_variant_set_field_string(&arg_identity_extra, "realName", optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set realName field: %m");
+
+                        break;
+
+                case 'd': {
+                        _cleanup_free_ char *hd = NULL;
+
+                        if (isempty(optarg)) {
+                                r = drop_from_identity("homeDirectory");
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        r = parse_path_argument_and_warn(optarg, false, &hd);
+                        if (r < 0)
+                                return r;
+
+                        if (!valid_home(hd))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Home directory '%s' not valid.", hd);
+
+                        r = json_variant_set_field_string(&arg_identity_extra, "homeDirectory", hd);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set homeDirectory field: %m");
+
+                        break;
+                }
+
+                case ARG_REALM:
+                        if (isempty(optarg)) {
+                                r = drop_from_identity("realm");
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        r = dns_name_is_valid(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to determine whether realm '%s' is a valid DNS domain: %m", optarg);
+                        if (r == 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Realm '%s' is not a valid DNS domain: %m", optarg);
+
+                        r = json_variant_set_field_string(&arg_identity_extra, "realm", optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set realm field: %m");
+                        break;
+
+                case ARG_EMAIL_ADDRESS:
+                case ARG_LOCATION:
+                case ARG_ICON_NAME:
+                case ARG_CIFS_USER_NAME:
+                case ARG_CIFS_DOMAIN:
+                case ARG_CIFS_SERVICE: {
+
+                        const char *field =
+                                c == ARG_EMAIL_ADDRESS ? "emailAddress" :
+                                     c == ARG_LOCATION ? "location" :
+                                    c == ARG_ICON_NAME ? "iconName" :
+                               c == ARG_CIFS_USER_NAME ? "cifsUserName" :
+                                  c == ARG_CIFS_DOMAIN ? "cifsDomain" :
+                                 c == ARG_CIFS_SERVICE ? "cifsService" :
+                                                         NULL;
+
+                        assert(field);
+
+                        if (isempty(optarg)) {
+                                r = drop_from_identity(field);
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        r = json_variant_set_field_string(&arg_identity_extra, field, optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set %s field: %m", field);
+
+                        break;
+                }
+
+                case ARG_PASSWORD_HINT:
+                        if (isempty(optarg)) {
+                                r = drop_from_identity("passwordHint");
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        r = json_variant_set_field_string(&arg_identity_extra_privileged, "passwordHint", optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set passwordHint field: %m");
+
+                        string_erase(optarg);
+                        break;
+
+                case ARG_NICE: {
+                        int nc;
+
+                        if (isempty(optarg)) {
+                                r = drop_from_identity("niceLevel");
+                                if (r < 0)
+                                        return r;
+                                break;
+                        }
+
+                        r = parse_nice(optarg, &nc);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse nice level: %s", optarg);
+
+                        r = json_variant_set_field_integer(&arg_identity_extra, "niceLevel", nc);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set niceLevel field: %m");
+
+                        break;
+                }
+
+                case ARG_RLIMIT: {
+                        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *jcur = NULL, *jmax = NULL;
+                        _cleanup_free_ char *field = NULL, *t = NULL;
+                        const char *eq;
+                        struct rlimit rl;
+                        int l;
+
+                        if (isempty(optarg)) {
+                                /* Remove all resource limits */
+
+                                r = drop_from_identity("resourceLimits");
+                                if (r < 0)
+                                        return r;
+
+                                arg_identity_filter_rlimits = strv_free(arg_identity_filter_rlimits);
+                                arg_identity_extra_rlimits = json_variant_unref(arg_identity_extra_rlimits);
+                                break;
+                        }
+
+                        eq = strchr(optarg, '=');
+                        if (!eq)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Can't parse resource limit assignment: %s", optarg);
+
+                        field = strndup(optarg, eq - optarg);
+                        if (!field)
+                                return log_oom();
+
+                        l = rlimit_from_string_harder(field);
+                        if (l < 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown resource limit type: %s", field);
+
+                        if (isempty(eq + 1)) {
+                                /* Remove only the specific rlimit */
+
+                                r = strv_extend(&arg_identity_filter_rlimits, rlimit_to_string(l));
+                                if (r < 0)
+                                        return r;
+
+                                r = json_variant_filter(&arg_identity_extra_rlimits, STRV_MAKE(field));
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to filter JSON identity data: %m");
+
+                                break;
+                        }
+
+                        r = rlimit_parse(l, eq + 1, &rl);
+                        if (r < 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse resource limit value: %s", eq + 1);
+
+                        r = rl.rlim_cur == RLIM_INFINITY ? json_variant_new_null(&jcur) : json_variant_new_unsigned(&jcur, rl.rlim_cur);
+                        if (r < 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to allocate current integer: %m");
+
+                        r = rl.rlim_max == RLIM_INFINITY ? json_variant_new_null(&jmax) : json_variant_new_unsigned(&jmax, rl.rlim_max);
+                        if (r < 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to allocate maximum integer: %m");
+
+                        r = json_build(&v,
+                                       JSON_BUILD_OBJECT(
+                                                       JSON_BUILD_PAIR("cur", JSON_BUILD_VARIANT(jcur)),
+                                                       JSON_BUILD_PAIR("max", JSON_BUILD_VARIANT(jmax))));
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to build resource limit: %m");
+
+                        t = strjoin("RLIMIT_", rlimit_to_string(l));
+                        if (!t)
+                                return log_oom();
+
+                        r = json_variant_set_field(&arg_identity_extra_rlimits, t, v);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set %s field: %m", rlimit_to_string(l));
+
+                        break;
+                }
+
+                case 'u': {
+                        uid_t uid;
+
+                        if (isempty(optarg)) {
+                                r = drop_from_identity("uid");
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        r = parse_uid(optarg, &uid);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse UID '%s'.", optarg);
+
+                        if (uid_is_system(uid))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID " UID_FMT " is in system range, refusing.", uid);
+                        if (uid_is_dynamic(uid))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID " UID_FMT " is in dynamic range, refusing.", uid);
+                        if (uid == UID_NOBODY)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "UID " UID_FMT " is nobody UID, refusing.", uid);
+
+                        r = json_variant_set_field_unsigned(&arg_identity_extra, "uid", uid);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set realm field: %m");
+
+                        break;
+                }
+
+                case 'k':
+                case ARG_IMAGE_PATH: {
+                        const char *field = c == 'k' ? "skeletonDirectory" : "imagePath";
+                        _cleanup_free_ char *v = NULL;
+
+                        if (isempty(optarg)) {
+                                r = drop_from_identity(field);
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        r = parse_path_argument_and_warn(optarg, false, &v);
+                        if (r < 0)
+                                return r;
+
+                        r = json_variant_set_field_string(&arg_identity_extra_this_machine, field, v);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set %s field: %m", v);
+
+                        break;
+                }
+
+                case 's':
+                        if (isempty(optarg)) {
+                                r = drop_from_identity("shell");
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        if (!valid_shell(optarg))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Shell '%s' not valid.", optarg);
+
+                        r = json_variant_set_field_string(&arg_identity_extra, "shell", optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set shell field: %m");
+
+                        break;
+
+                case ARG_SETENV: {
+                        _cleanup_free_ char **l = NULL, **k = NULL;
+                        _cleanup_(json_variant_unrefp) JsonVariant *ne = NULL;
+                        JsonVariant *e;
+
+                        if (isempty(optarg)) {
+                                r = drop_from_identity("environment");
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        if (!env_assignment_is_valid(optarg))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Environment assignment '%s' not valid.", optarg);
+
+                        e = json_variant_by_key(arg_identity_extra, "environment");
+                        if (e) {
+                                r = json_variant_strv(e, &l);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse JSON environment field: %m");
+                        }
+
+                        k = strv_env_set(l, optarg);
+                        if (!k)
+                                return log_oom();
+
+                        strv_sort(k);
+
+                        r = json_variant_new_array_strv(&ne, k);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to allocate environment list JSON: %m");
+
+                        r = json_variant_set_field(&arg_identity_extra, "environment", ne);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set environent list: %m");
+
+                        break;
+                }
+
+                case ARG_TIMEZONE:
+
+                        if (isempty(optarg)) {
+                                r = drop_from_identity("timeZone");
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        if (!timezone_is_valid(optarg, LOG_DEBUG))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Timezone '%s' is not valid.", optarg);
+
+                        r = json_variant_set_field_string(&arg_identity_extra, "timeZone", optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set timezone field: %m");
+
+                        break;
+
+                case ARG_LANGUAGE:
+                        if (isempty(optarg)) {
+                                r = drop_from_identity("language");
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        if (!locale_is_valid(optarg))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale '%s' is not valid.", 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");
+
+                        break;
+
+                case ARG_NOSUID:
+                case ARG_NODEV:
+                case ARG_NOEXEC:
+                case ARG_LOCKED:
+                case ARG_KILL_PROCESSES:
+                case ARG_ENFORCE_PASSWORD_POLICY:
+                case ARG_AUTO_LOGIN:
+                case ARG_PASSWORD_CHANGE_NOW: {
+                        const char *field =
+                                                 c == ARG_LOCKED ? "locked" :
+                                                 c == ARG_NOSUID ? "mountNoSuid" :
+                                                  c == ARG_NODEV ? "mountNoDevices" :
+                                                 c == ARG_NOEXEC ? "mountNoExecute" :
+                                         c == ARG_KILL_PROCESSES ? "killProcesses" :
+                                c == ARG_ENFORCE_PASSWORD_POLICY ? "enforcePasswordPolicy" :
+                                             c == ARG_AUTO_LOGIN ? "autoLogin" :
+                                    c == ARG_PASSWORD_CHANGE_NOW ? "passwordChangeNow" :
+                                                                   NULL;
+
+                        assert(field);
+
+                        if (isempty(optarg)) {
+                                r = drop_from_identity(field);
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse %s boolean: %m", field);
+
+                        r = json_variant_set_field_boolean(&arg_identity_extra, field, r > 0);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set %s field: %m", field);
+
+                        break;
+                }
+
+                case 'P':
+                        r = json_variant_set_field_boolean(&arg_identity_extra, "enforcePasswordPolicy", false);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set enforcePasswordPolicy field: %m");
+
+                        break;
+
+                case ARG_DISK_SIZE:
+                        if (isempty(optarg)) {
+                                r = drop_from_identity("diskSize");
+                                if (r < 0)
+                                        return r;
+
+                                r = drop_from_identity("diskSizeRelative");
+                                if (r < 0)
+                                        return r;
+
+                                arg_disk_size = arg_disk_size_relative = UINT64_MAX;
+                                break;
+                        }
+
+                        r = parse_permille(optarg);
+                        if (r < 0) {
+                                r = parse_size(optarg, 1024, &arg_disk_size);
+                                if (r < 0)
+                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Disk size '%s' not valid.", optarg);
+
+                                r = drop_from_identity("diskSizeRelative");
+                                if (r < 0)
+                                        return r;
+
+                                r = json_variant_set_field_unsigned(&arg_identity_extra_this_machine, "diskSize", arg_disk_size);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to set diskSize field: %m");
+
+                                arg_disk_size_relative = UINT64_MAX;
+                        } else {
+                                /* Normalize to UINT32_MAX == 100% */
+                                arg_disk_size_relative = (uint64_t) r * UINT32_MAX / 1000U;
+
+                                r = drop_from_identity("diskSize");
+                                if (r < 0)
+                                        return r;
+
+                                r = json_variant_set_field_unsigned(&arg_identity_extra_this_machine, "diskSizeRelative", arg_disk_size_relative);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to set diskSizeRelative field: %m");
+
+                                arg_disk_size = UINT64_MAX;
+                        }
+
+                        break;
+
+                case ARG_ACCESS_MODE: {
+                        mode_t mode;
+
+                        if (isempty(optarg)) {
+                                r = drop_from_identity("accessMode");
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        r = parse_mode(optarg, &mode);
+                        if (r < 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Access mode '%s' not valid.", optarg);
+
+                        r = json_variant_set_field_unsigned(&arg_identity_extra, "accessMode", mode);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set access mode field: %m");
+
+                        break;
+                }
+
+                case ARG_LUKS_DISCARD:
+                        if (isempty(optarg)) {
+                                r = drop_from_identity("luksDiscard");
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --luks-discard= parameter: %s", optarg);
+
+                        r = json_variant_set_field_boolean(&arg_identity_extra, "luksDiscard", r);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set discard field: %m");
+
+                        break;
+
+                case ARG_LUKS_VOLUME_KEY_SIZE:
+                case ARG_LUKS_PBKDF_PARALLEL_THREADS:
+                case ARG_RATE_LIMIT_BURST: {
+                        const char *field =
+                                       c == ARG_LUKS_VOLUME_KEY_SIZE ? "luksVolumeKeySize" :
+                                c == ARG_LUKS_PBKDF_PARALLEL_THREADS ? "luksPbkdfParallelThreads" :
+                                           c == ARG_RATE_LIMIT_BURST ? "rateLimitBurst" : NULL;
+                        unsigned n;
+
+                        assert(field);
+
+                        if (isempty(optarg)) {
+                                r = drop_from_identity(field);
+                                if (r < 0)
+                                        return r;
+                        }
+
+                        r = safe_atou(optarg, &n);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse %s parameter: %s", field, optarg);
+
+                        r = json_variant_set_field_unsigned(&arg_identity_extra, field, n);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set %s field: %m", field);
+
+                        break;
+                }
+
+                case ARG_UMASK: {
+                        mode_t m;
+
+                        if (isempty(optarg)) {
+                                r = drop_from_identity("umask");
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        r = parse_mode(optarg, &m);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse umask: %m");
+
+                        r = json_variant_set_field_integer(&arg_identity_extra, "umask", m);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set umask field: %m");
+
+                        break;
+                }
+
+                case ARG_SSH_AUTHORIZED_KEYS: {
+                        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+                        _cleanup_(strv_freep) char **l = NULL, **add = NULL;
+
+                        if (isempty(optarg)) {
+                                r = drop_from_identity("sshAuthorizedKeys");
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        if (optarg[0] == '@') {
+                                _cleanup_fclose_ FILE *f = NULL;
+
+                                /* If prefixed with '@' read from a file */
+
+                                f = fopen(optarg+1, "re");
+                                if (!f)
+                                        return log_error_errno(errno, "Failed to open '%s': %m", optarg+1);
+
+                                for (;;) {
+                                        _cleanup_free_ char *line = NULL;
+
+                                        r = read_line(f, LONG_LINE_MAX, &line);
+                                        if (r < 0)
+                                                return log_error_errno(r, "Faile dto read from '%s': %m", optarg+1);
+                                        if (r == 0)
+                                                break;
+
+                                        if (isempty(line))
+                                                continue;
+
+                                        if (line[0] == '#')
+                                                continue;
+
+                                        r = strv_consume(&add, TAKE_PTR(line));
+                                        if (r < 0)
+                                                return log_oom();
+                                }
+                        } else {
+                                /* Otherwise, assume it's a literal key. Let's do some superficial checks
+                                 * before accept it though. */
+
+                                if (string_has_cc(optarg, NULL))
+                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Authorized key contains control characters, refusing.");
+                                if (optarg[0] == '#')
+                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified key is a comment?");
+
+                                add = strv_new(optarg);
+                                if (!add)
+                                        return log_oom();
+                        }
+
+                        v = json_variant_ref(json_variant_by_key(arg_identity_extra_privileged, "sshAuthorizedKeys"));
+                        if (v) {
+                                r = json_variant_strv(v, &l);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse SSH authorized keys list: %m");
+                        }
+
+                        r = strv_extend_strv(&l, add, true);
+                        if (r < 0)
+                                return log_oom();
+
+                        v = json_variant_unref(v);
+
+                        r = json_variant_new_array_strv(&v, l);
+                        if (r < 0)
+                                return log_oom();
+
+                        r = json_variant_set_field(&arg_identity_extra_privileged, "sshAuthorizedKeys", v);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set authorized keys: %m");
+
+                        break;
+                }
+
+                case ARG_NOT_BEFORE:
+                case ARG_NOT_AFTER:
+                case 'e': {
+                        const char *field;
+                        usec_t n;
+
+                        field =           c == ARG_NOT_BEFORE ? "notBeforeUSec" :
+                                IN_SET(c, ARG_NOT_AFTER, 'e') ? "notAfterUSec" : NULL;
+
+                        assert(field);
+
+                        if (isempty(optarg)) {
+                                r = drop_from_identity(field);
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        /* Note the minor discrepancy regarding -e parsing here: we support that for compat
+                         * reasons, and in the original useradd(8) implementation it accepts dates in the
+                         * format YYYY-MM-DD. Coincidentally, we accept dates formatted like that too, but
+                         * with greater precision. */
+                        r = parse_timestamp(optarg, &n);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse %s parameter: %m", field);
+
+                        r = json_variant_set_field_unsigned(&arg_identity_extra, field, n);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set %s field: %m", field);
+                        break;
+                }
+
+                case ARG_PASSWORD_CHANGE_MIN:
+                case ARG_PASSWORD_CHANGE_MAX:
+                case ARG_PASSWORD_CHANGE_WARN:
+                case ARG_PASSWORD_CHANGE_INACTIVE: {
+                        const char *field;
+                        usec_t n;
+
+                        field =      c == ARG_PASSWORD_CHANGE_MIN ? "passwordChangeMinUSec" :
+                                     c == ARG_PASSWORD_CHANGE_MAX ? "passwordChangeMaxUSec" :
+                                    c == ARG_PASSWORD_CHANGE_WARN ? "passwordChangeWarnUSec" :
+                                c == ARG_PASSWORD_CHANGE_INACTIVE ? "passwordChangeInactiveUSec" :
+                                                                    NULL;
+
+                        assert(field);
+
+                        if (isempty(optarg)) {
+                                r = drop_from_identity(field);
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        r = parse_sec(optarg, &n);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse %s parameter: %m", field);
+
+                        r = json_variant_set_field_unsigned(&arg_identity_extra, field, n);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set %s field: %m", field);
+                        break;
+                }
+
+                case ARG_STORAGE:
+                case ARG_FS_TYPE:
+                case ARG_LUKS_CIPHER:
+                case ARG_LUKS_CIPHER_MODE:
+                case ARG_LUKS_PBKDF_TYPE:
+                case ARG_LUKS_PBKDF_HASH_ALGORITHM: {
+
+                        const char *field =
+                                                  c == ARG_STORAGE ? "storage" :
+                                                  c == ARG_FS_TYPE ? "fileSystemType" :
+                                              c == ARG_LUKS_CIPHER ? "luksCipher" :
+                                         c == ARG_LUKS_CIPHER_MODE ? "luksCipherMode" :
+                                          c == ARG_LUKS_PBKDF_TYPE ? "luksPbkdfType" :
+                                c == ARG_LUKS_PBKDF_HASH_ALGORITHM ? "luksPbkdfHashAlgorithm" : NULL;
+
+                        assert(field);
+
+                        if (isempty(optarg)) {
+                                r = drop_from_identity(field);
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        if (!string_is_safe(optarg))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Parameter for %s field not valid: %s", field, optarg);
+
+                        r = json_variant_set_field_string(
+                                        IN_SET(c, ARG_STORAGE, ARG_FS_TYPE) ?
+                                        &arg_identity_extra_this_machine :
+                                        &arg_identity_extra, field, optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set %s field: %m", field);
+
+                        break;
+                }
+
+                case ARG_LUKS_PBKDF_TIME_COST:
+                case ARG_RATE_LIMIT_INTERVAL:
+                case ARG_STOP_DELAY: {
+                        const char *field =
+                                c == ARG_LUKS_PBKDF_TIME_COST ? "luksPbkdfTimeCostUSec" :
+                                 c == ARG_RATE_LIMIT_INTERVAL ? "rateLimitIntervalUSec" :
+                                          c == ARG_STOP_DELAY ? "stopDelayUSec" :
+                                                                NULL;
+                        usec_t t;
+
+                        assert(field);
+
+                        if (isempty(optarg)) {
+                                r = drop_from_identity(field);
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        r = parse_sec(optarg, &t);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse %s field: %s", field, optarg);
+
+                        r = json_variant_set_field_unsigned(&arg_identity_extra, field, t);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set %s field: %m", field);
+
+                        break;
+                }
+
+                case 'G': {
+                        const char *p = optarg;
+
+                        if (isempty(p)) {
+                                r = drop_from_identity("memberOf");
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        for (;;) {
+                                _cleanup_(json_variant_unrefp) JsonVariant *mo = NULL;
+                                _cleanup_strv_free_ char **list = NULL;
+                                _cleanup_free_ char *word = NULL;
+
+                                r = extract_first_word(&p, &word, ",", 0);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse group list: %m");
+                                if (r == 0)
+                                        break;
+
+                                if (!valid_user_group_name(word))
+                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid group name %s.", word);
+
+                                mo = json_variant_ref(json_variant_by_key(arg_identity_extra, "memberOf"));
+
+                                r = json_variant_strv(mo, &list);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse group list: %m");
+
+                                r = strv_extend(&list, word);
+                                if (r < 0)
+                                        return log_oom();
+
+                                strv_sort(list);
+                                strv_uniq(list);
+
+                                mo = json_variant_unref(mo);
+                                r = json_variant_new_array_strv(&mo, list);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to create group list JSON: %m");
+
+                                r = json_variant_set_field(&arg_identity_extra, "memberOf", mo);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to update group list: %m");
+                        }
+
+                        break;
+                }
+
+                case ARG_TASKS_MAX: {
+                        uint64_t u;
+
+                        if (isempty(optarg)) {
+                                r = drop_from_identity("tasksMax");
+                                if (r < 0)
+                                        return r;
+                                break;
+                        }
+
+                        r = safe_atou64(optarg, &u);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --tasks-max= parameter: %s", optarg);
+
+                        r = json_variant_set_field_unsigned(&arg_identity_extra, "tasksMax", u);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set tasksMax field: %m");
+
+                        break;
+                }
+
+                case ARG_MEMORY_MAX:
+                case ARG_MEMORY_HIGH:
+                case ARG_LUKS_PBKDF_MEMORY_COST: {
+                        const char *field =
+                                            c == ARG_MEMORY_MAX ? "memoryMax" :
+                                           c == ARG_MEMORY_HIGH ? "memoryHigh" :
+                                c == ARG_LUKS_PBKDF_MEMORY_COST ? "luksPbkdfMemoryCost" : NULL;
+
+                        uint64_t u;
+
+                        assert(field);
+
+                        if (isempty(optarg)) {
+                                r = drop_from_identity(field);
+                                if (r < 0)
+                                        return r;
+                                break;
+                        }
+
+                        r = parse_size(optarg, 1024, &u);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse %s parameter: %s", field, optarg);
+
+                        r = json_variant_set_field_unsigned(&arg_identity_extra_this_machine, field, u);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set %s field: %m", field);
+
+                        break;
+                }
+
+                case ARG_CPU_WEIGHT:
+                case ARG_IO_WEIGHT: {
+                        const char *field = c == ARG_CPU_WEIGHT ? "cpuWeight" :
+                                            c == ARG_IO_WEIGHT ? "ioWeight" : NULL;
+                        uint64_t u;
+
+                        assert(field);
+
+                        if (isempty(optarg)) {
+                                r = drop_from_identity(field);
+                                if (r < 0)
+                                        return r;
+                                break;
+                        }
+
+                        r = safe_atou64(optarg, &u);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --cpu-weight=/--io-weight= parameter: %s", optarg);
+
+                        if (!CGROUP_WEIGHT_IS_OK(u))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Weight %" PRIu64 " is out of valid weight range.", u);
+
+                        r = json_variant_set_field_unsigned(&arg_identity_extra, field, u);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set %s field: %m", field);
+
+                        break;
+                }
+
+                case ARG_PKCS11_TOKEN_URI: {
+                        const char *p;
+
+                        /* If --pkcs11-token-uri= is specified we always drop everything old */
+                        FOREACH_STRING(p, "pkcs11TokenUri", "pkcs11EncryptedKey") {
+                                r = drop_from_identity(p);
+                                if (r < 0)
+                                        return r;
+                        }
+
+                        if (isempty(optarg)) {
+                                arg_pkcs11_token_uri = strv_free(arg_pkcs11_token_uri);
+                                break;
+                        }
+
+                        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);
+                        if (r < 0)
+                                return r;
+
+                        strv_uniq(arg_pkcs11_token_uri);
+                        break;
+                }
+
+                case 'j':
+                        arg_json = true;
+                        arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
+                        break;
+
+                case ARG_JSON:
+                        if (streq(optarg, "pretty")) {
+                                arg_json = true;
+                                arg_json_format_flags = JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR_AUTO;
+                        } else if (streq(optarg, "short")) {
+                                arg_json = true;
+                                arg_json_format_flags = JSON_FORMAT_NEWLINE;
+                        } else if (streq(optarg, "off")) {
+                                arg_json = false;
+                                arg_json_format_flags = 0;
+                        } else if (streq(optarg, "help")) {
+                                puts("pretty\n"
+                                     "short\n"
+                                     "off");
+                                return 0;
+                        } else
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown argument to --json=: %s", optarg);
+
+                        break;
+
+                case 'E':
+                        if (arg_export_format == EXPORT_FORMAT_FULL)
+                                arg_export_format = EXPORT_FORMAT_STRIPPED;
+                        else if (arg_export_format == EXPORT_FORMAT_STRIPPED)
+                                arg_export_format = EXPORT_FORMAT_MINIMAL;
+                        else
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specifying -E more than twice is not supported.");
+
+                        arg_json = true;
+                        if (arg_json_format_flags == 0)
+                                arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
+                        break;
+
+                case ARG_EXPORT_FORMAT:
+                        if (streq(optarg, "full"))
+                                arg_export_format = EXPORT_FORMAT_FULL;
+                        else if (streq(optarg, "stripped"))
+                                arg_export_format = EXPORT_FORMAT_STRIPPED;
+                        else if (streq(optarg, "minimal"))
+                                arg_export_format = EXPORT_FORMAT_MINIMAL;
+                        else if (streq(optarg, "help")) {
+                                puts("full\n"
+                                     "stripped\n"
+                                     "minimal");
+                                return 0;
+                        }
+
+                        break;
+
+                case ARG_AND_RESIZE:
+                        arg_and_resize = true;
+                        break;
+
+                case ARG_AND_CHANGE_PASSWORD:
+                        arg_and_change_password = true;
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        assert_not_reached("Unhandled option");
+                }
+        }
+
+        if (!strv_isempty(arg_pkcs11_token_uri))
+                arg_and_change_password = true;
+
+        if (arg_disk_size != UINT64_MAX || arg_disk_size_relative != UINT64_MAX)
+                arg_and_resize = true;
+
+        return 1;
+}
+
+static int run(int argc, char *argv[]) {
+        static const Verb verbs[] = {
+                { "help",         VERB_ANY, VERB_ANY, 0,            help                },
+                { "list",         VERB_ANY, 1,        VERB_DEFAULT, list_homes          },
+                { "activate",     2,        VERB_ANY, 0,            activate_home       },
+                { "deactivate",   2,        VERB_ANY, 0,            deactivate_home     },
+                { "inspect",      VERB_ANY, VERB_ANY, 0,            inspect_home        },
+                { "authenticate", VERB_ANY, VERB_ANY, 0,            authenticate_home   },
+                { "create",       VERB_ANY, 2,        0,            create_home         },
+                { "remove",       2,        VERB_ANY, 0,            remove_home         },
+                { "update",       VERB_ANY, 2,        0,            update_home         },
+                { "passwd",       VERB_ANY, 2,        0,            passwd_home         },
+                { "resize",       2,        3,        0,            resize_home         },
+                { "lock",         2,        VERB_ANY, 0,            lock_home           },
+                { "unlock",       2,        VERB_ANY, 0,            unlock_home         },
+                { "with",         2,        VERB_ANY, 0,            with_home           },
+                { "lock-all",     VERB_ANY, 1,        0,            lock_all_homes      },
+                {}
+        };
+
+        int r;
+
+        log_show_color(true);
+        log_parse_environment();
+        log_open();
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r;
+
+        return dispatch_verb(argc, argv, verbs, NULL);
+}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/home/homed-bus.c b/src/home/homed-bus.c
new file mode 100644 (file)
index 0000000..0193089
--- /dev/null
@@ -0,0 +1,64 @@
+#include "homed-bus.h"
+#include "strv.h"
+
+int bus_message_read_secret(sd_bus_message *m, UserRecord **ret, sd_bus_error *error) {
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *full = NULL;
+        _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+        unsigned line = 0, column = 0;
+        const char *json;
+        int r;
+
+        assert(ret);
+
+        r = sd_bus_message_read(m, "s", &json);
+        if (r < 0)
+                return r;
+
+        r = json_parse(json, JSON_PARSE_SENSITIVE, &v, &line, &column);
+        if (r < 0)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to parse JSON secret record at %u:%u: %m", line, column);
+
+        r = json_build(&full, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("secret", JSON_BUILD_VARIANT(v))));
+        if (r < 0)
+                return r;
+
+        hr = user_record_new();
+        if (!hr)
+                return -ENOMEM;
+
+        r = user_record_load(hr, full, USER_RECORD_REQUIRE_SECRET);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(hr);
+        return 0;
+}
+
+int bus_message_read_home_record(sd_bus_message *m, UserRecordLoadFlags flags, UserRecord **ret, sd_bus_error *error) {
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+        unsigned line = 0, column = 0;
+        const char *json;
+        int r;
+
+        assert(ret);
+
+        r = sd_bus_message_read(m, "s", &json);
+        if (r < 0)
+                return r;
+
+        r = json_parse(json, JSON_PARSE_SENSITIVE, &v, &line, &column);
+        if (r < 0)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to parse JSON identity record at %u:%u: %m", line, column);
+
+        hr = user_record_new();
+        if (!hr)
+                return -ENOMEM;
+
+        r = user_record_load(hr, v, flags);
+        if (r < 0)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "JSON data is not a valid identity record");
+
+        *ret = TAKE_PTR(hr);
+        return 0;
+}
diff --git a/src/home/homed-bus.h b/src/home/homed-bus.h
new file mode 100644 (file)
index 0000000..20f13b4
--- /dev/null
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+#include "user-record.h"
+#include "json.h"
+
+int bus_message_read_secret(sd_bus_message *m, UserRecord **ret, sd_bus_error *error);
+int bus_message_read_home_record(sd_bus_message *m, UserRecordLoadFlags flags, UserRecord **ret, sd_bus_error *error);
diff --git a/src/home/homed-home-bus.c b/src/home/homed-home-bus.c
new file mode 100644 (file)
index 0000000..02a87a5
--- /dev/null
@@ -0,0 +1,877 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <linux/capability.h>
+
+#include "bus-common-errors.h"
+#include "bus-polkit.h"
+#include "fd-util.h"
+#include "homed-bus.h"
+#include "homed-home-bus.h"
+#include "homed-home.h"
+#include "strv.h"
+#include "user-record-util.h"
+#include "user-util.h"
+
+static int property_get_unix_record(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Home *h = userdata;
+
+        assert(bus);
+        assert(reply);
+        assert(h);
+
+        return sd_bus_message_append(
+                        reply, "(suusss)",
+                        h->user_name,
+                        (uint32_t) h->uid,
+                        h->record ? (uint32_t) user_record_gid(h->record) : GID_INVALID,
+                        h->record ? user_record_real_name(h->record) : NULL,
+                        h->record ? user_record_home_directory(h->record) : NULL,
+                        h->record ? user_record_shell(h->record) : NULL);
+}
+
+static int property_get_state(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Home *h = userdata;
+
+        assert(bus);
+        assert(reply);
+        assert(h);
+
+        return sd_bus_message_append(reply, "s", home_state_to_string(home_get_state(h)));
+}
+
+int bus_home_client_is_trusted(Home *h, sd_bus_message *message) {
+        _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+        uid_t euid;
+        int r;
+
+        assert(h);
+
+        if (!message)
+                return -EINVAL;
+
+        r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_creds_get_euid(creds, &euid);
+        if (r < 0)
+                return r;
+
+        return euid == 0 || h->uid == euid;
+}
+
+int bus_home_get_record_json(
+                Home *h,
+                sd_bus_message *message,
+                char **ret,
+                bool *ret_incomplete) {
+
+        _cleanup_(user_record_unrefp) UserRecord *augmented = NULL;
+        UserRecordLoadFlags flags;
+        int r, trusted;
+
+        assert(h);
+        assert(ret);
+
+        trusted = bus_home_client_is_trusted(h, message);
+        if (trusted < 0) {
+                log_warning_errno(trusted, "Failed to determine whether client is trusted, assuming untrusted.");
+                trusted = false;
+        }
+
+        flags = USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_BINDING|USER_RECORD_STRIP_SECRET|USER_RECORD_ALLOW_STATUS|USER_RECORD_ALLOW_SIGNATURE;
+        if (trusted)
+                flags |= USER_RECORD_ALLOW_PRIVILEGED;
+        else
+                flags |= USER_RECORD_STRIP_PRIVILEGED;
+
+        r = home_augment_status(h, flags, &augmented);
+        if (r < 0)
+                return r;
+
+        r = json_variant_format(augmented->json, 0, ret);
+        if (r < 0)
+                return r;
+
+        if (ret_incomplete)
+                *ret_incomplete = augmented->incomplete;
+
+        return 0;
+}
+
+static int property_get_user_record(
+                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 *json = NULL;
+        Home *h = userdata;
+        bool incomplete;
+        int r;
+
+        assert(bus);
+        assert(reply);
+        assert(h);
+
+        r = bus_home_get_record_json(h, sd_bus_get_current_message(bus), &json, &incomplete);
+        if (r < 0)
+                return r;
+
+        return sd_bus_message_append(reply, "(sb)", json, incomplete);
+}
+
+int bus_home_method_activate(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+        Home *h = userdata;
+        int r;
+
+        assert(message);
+        assert(h);
+
+        r = bus_message_read_secret(message, &secret, error);
+        if (r < 0)
+                return r;
+
+        r = home_activate(h, secret, error);
+        if (r < 0)
+                return r;
+
+        assert(r == 0);
+        assert(!h->current_operation);
+
+        /* The operation is now in process, keep track of this message so that we can later reply to it. */
+        r = home_set_current_message(h, message);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+int bus_home_method_deactivate(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Home *h = userdata;
+        int r;
+
+        assert(message);
+        assert(h);
+
+        r = home_deactivate(h, false, error);
+        if (r < 0)
+                return r;
+
+        assert(r == 0);
+        assert(!h->current_operation);
+
+        r = home_set_current_message(h, message);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+int bus_home_method_unregister(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Home *h = userdata;
+        int r;
+
+        assert(message);
+        assert(h);
+
+        r = bus_verify_polkit_async(
+                        message,
+                        CAP_SYS_ADMIN,
+                        "org.freedesktop.home1.remove-home",
+                        NULL,
+                        true,
+                        UID_INVALID,
+                        &h->manager->polkit_registry,
+                        error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* Will call us back */
+
+        r = home_unregister(h, error);
+        if (r < 0)
+                return r;
+
+        assert(r > 0);
+
+        /* Note that home_unregister() destroyed 'h' here, so no more accesses */
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_home_method_realize(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+        Home *h = userdata;
+        int r;
+
+        assert(message);
+        assert(h);
+
+        r = bus_message_read_secret(message, &secret, error);
+        if (r < 0)
+                return r;
+
+        r = bus_verify_polkit_async(
+                        message,
+                        CAP_SYS_ADMIN,
+                        "org.freedesktop.home1.create-home",
+                        NULL,
+                        true,
+                        UID_INVALID,
+                        &h->manager->polkit_registry,
+                        error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* Will call us back */
+
+        r = home_create(h, secret, error);
+        if (r < 0)
+                return r;
+
+        assert(r == 0);
+        assert(!h->current_operation);
+
+        h->unregister_on_failure = false;
+
+        r = home_set_current_message(h, message);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+int bus_home_method_remove(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Home *h = userdata;
+        int r;
+
+        assert(message);
+        assert(h);
+
+        r = bus_verify_polkit_async(
+                        message,
+                        CAP_SYS_ADMIN,
+                        "org.freedesktop.home1.remove-home",
+                        NULL,
+                        true,
+                        UID_INVALID,
+                        &h->manager->polkit_registry,
+                        error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* Will call us back */
+
+        r = home_remove(h, error);
+        if (r < 0)
+                return r;
+        if (r > 0) /* Done already. Note that home_remove() destroyed 'h' here, so no more accesses */
+                return sd_bus_reply_method_return(message, NULL);
+
+        assert(!h->current_operation);
+
+        r = home_set_current_message(h, message);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+int bus_home_method_fixate(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+        Home *h = userdata;
+        int r;
+
+        assert(message);
+        assert(h);
+
+        r = bus_message_read_secret(message, &secret, error);
+        if (r < 0)
+                return r;
+
+        r = home_fixate(h, secret, error);
+        if (r < 0)
+                return r;
+
+        assert(r == 0);
+        assert(!h->current_operation);
+
+        r = home_set_current_message(h, message);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+int bus_home_method_authenticate(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+        Home *h = userdata;
+        int r;
+
+        assert(message);
+        assert(h);
+
+        r = bus_message_read_secret(message, &secret, error);
+        if (r < 0)
+                return r;
+
+        r = bus_verify_polkit_async(
+                        message,
+                        CAP_SYS_ADMIN,
+                        "org.freedesktop.home1.authenticate-home",
+                        NULL,
+                        true,
+                        h->uid,
+                        &h->manager->polkit_registry,
+                        error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* Will call us back */
+
+        r = home_authenticate(h, secret, error);
+        if (r < 0)
+                return r;
+
+        assert(r == 0);
+        assert(!h->current_operation);
+
+        r = home_set_current_message(h, message);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+int bus_home_method_update_record(Home *h, sd_bus_message *message, UserRecord *hr, sd_bus_error *error) {
+        int r;
+
+        assert(h);
+        assert(message);
+        assert(hr);
+
+        r = user_record_is_supported(hr, error);
+        if (r < 0)
+                return r;
+
+        r = bus_verify_polkit_async(
+                        message,
+                        CAP_SYS_ADMIN,
+                        "org.freedesktop.home1.update-home",
+                        NULL,
+                        true,
+                        UID_INVALID,
+                        &h->manager->polkit_registry,
+                        error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* Will call us back */
+
+        r = home_update(h, hr, error);
+        if (r < 0)
+                return r;
+
+        assert(r == 0);
+        assert(!h->current_operation);
+
+        r = home_set_current_message(h, message);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+int bus_home_method_update(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+        Home *h = userdata;
+        int r;
+
+        assert(message);
+        assert(h);
+
+        r = bus_message_read_home_record(message, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_REQUIRE_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE, &hr, error);
+        if (r < 0)
+                return r;
+
+        return bus_home_method_update_record(h, message, hr, error);
+}
+
+int bus_home_method_resize(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+        Home *h = userdata;
+        uint64_t sz;
+        int r;
+
+        assert(message);
+        assert(h);
+
+        r = sd_bus_message_read(message, "t", &sz);
+        if (r < 0)
+                return r;
+
+        r = bus_message_read_secret(message, &secret, error);
+        if (r < 0)
+                return r;
+
+        r = bus_verify_polkit_async(
+                        message,
+                        CAP_SYS_ADMIN,
+                        "org.freedesktop.home1.resize-home",
+                        NULL,
+                        true,
+                        UID_INVALID,
+                        &h->manager->polkit_registry,
+                        error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* Will call us back */
+
+        r = home_resize(h, sz, secret, error);
+        if (r < 0)
+                return r;
+
+        assert(r == 0);
+        assert(!h->current_operation);
+
+        r = home_set_current_message(h, message);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+int bus_home_method_change_password(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(user_record_unrefp) UserRecord *new_secret = NULL, *old_secret = NULL;
+        Home *h = userdata;
+        int r;
+
+        assert(message);
+        assert(h);
+
+        r = bus_message_read_secret(message, &new_secret, error);
+        if (r < 0)
+                return r;
+
+        r = bus_message_read_secret(message, &old_secret, error);
+        if (r < 0)
+                return r;
+
+        r = bus_verify_polkit_async(
+                        message,
+                        CAP_SYS_ADMIN,
+                        "org.freedesktop.home1.passwd-home",
+                        NULL,
+                        true,
+                        h->uid,
+                        &h->manager->polkit_registry,
+                        error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* Will call us back */
+
+        r = home_passwd(h, new_secret, old_secret, error);
+        if (r < 0)
+                return r;
+
+        assert(r == 0);
+        assert(!h->current_operation);
+
+        r = home_set_current_message(h, message);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+int bus_home_method_lock(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Home *h = userdata;
+        int r;
+
+        assert(message);
+        assert(h);
+
+        r = home_lock(h, error);
+        if (r < 0)
+                return r;
+        if (r > 0) /* Done */
+                return sd_bus_reply_method_return(message, NULL);
+
+        /* The operation is now in process, keep track of this message so that we can later reply to it. */
+        assert(!h->current_operation);
+
+        r = home_set_current_message(h, message);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+int bus_home_method_unlock(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+        Home *h = userdata;
+        int r;
+
+        assert(message);
+        assert(h);
+
+        r = bus_message_read_secret(message, &secret, error);
+        if (r < 0)
+                return r;
+
+        r = home_unlock(h, secret, error);
+        if (r < 0)
+                return r;
+
+        assert(r == 0);
+        assert(!h->current_operation);
+
+        /* The operation is now in process, keep track of this message so that we can later reply to it. */
+        r = home_set_current_message(h, message);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+int bus_home_method_acquire(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+        _cleanup_(operation_unrefp) Operation *o = NULL;
+        _cleanup_close_ int fd = -1;
+        int r, please_suspend;
+        Home *h = userdata;
+
+        assert(message);
+        assert(h);
+
+        r = bus_message_read_secret(message, &secret, error);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_read(message, "b", &please_suspend);
+        if (r < 0)
+                return r;
+
+        /* This operation might not be something we can executed immediately, hence queue it */
+        fd = home_create_fifo(h, please_suspend);
+        if (fd < 0)
+                return sd_bus_reply_method_errnof(message, fd, "Failed to allocate fifo for %s: %m", h->user_name);
+
+        o = operation_new(OPERATION_ACQUIRE, message);
+        if (!o)
+                return -ENOMEM;
+
+        o->secret = TAKE_PTR(secret);
+        o->send_fd = TAKE_FD(fd);
+
+        r = home_schedule_operation(h, o, error);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+int bus_home_method_ref(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_close_ int fd = -1;
+        Home *h = userdata;
+        HomeState state;
+        int please_suspend, r;
+
+        assert(message);
+        assert(h);
+
+        r = sd_bus_message_read(message, "b", &please_suspend);
+        if (r < 0)
+                return r;
+
+        state = home_get_state(h);
+        switch (state) {
+        case HOME_ABSENT:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
+        case HOME_UNFIXATED:
+        case HOME_INACTIVE:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_ACTIVE, "Home %s not active.", h->user_name);
+        case HOME_LOCKED:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+        default:
+                if (HOME_STATE_IS_ACTIVE(state))
+                        break;
+
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
+        }
+
+        fd = home_create_fifo(h, please_suspend);
+        if (fd < 0)
+                return sd_bus_reply_method_errnof(message, fd, "Failed to allocate fifo for %s: %m", h->user_name);
+
+        return sd_bus_reply_method_return(message, "h", fd);
+}
+
+int bus_home_method_release(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(operation_unrefp) Operation *o = NULL;
+        Home *h = userdata;
+        int r;
+
+        assert(message);
+        assert(h);
+
+        o = operation_new(OPERATION_RELEASE, message);
+        if (!o)
+                return -ENOMEM;
+
+        r = home_schedule_operation(h, o, error);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+/* 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(
+                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;
+        uid_t uid;
+        Home *h;
+        int r;
+
+        r = sd_bus_path_decode(path, "/org/freedesktop/home1/home", &e);
+        if (r <= 0)
+                return 0;
+
+        if (parse_uid(e, &uid) >= 0)
+                h = hashmap_get(m->homes_by_uid, UID_TO_PTR(uid));
+        else
+                h = hashmap_get(m->homes_by_name, e);
+        if (!h)
+                return 0;
+
+        *found = h;
+        return 1;
+}
+
+int bus_home_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;
+        size_t k = 0;
+        Iterator i;
+        Home *h;
+        int r;
+
+        assert(nodes);
+
+        l = new0(char*, hashmap_size(m->homes_by_uid) + 1);
+        if (!l)
+                return -ENOMEM;
+
+        HASHMAP_FOREACH(h, m->homes_by_uid, i) {
+                r = bus_home_path(h, l + k);
+                if (r < 0)
+                        return r;
+        }
+
+        *nodes = TAKE_PTR(l);
+        return 1;
+}
+
+static int on_deferred_change(sd_event_source *s, void *userdata) {
+        _cleanup_free_ char *path = NULL;
+        Home *h = userdata;
+        int r;
+
+        assert(h);
+
+        h->deferred_change_event_source = sd_event_source_unref(h->deferred_change_event_source);
+
+        r = bus_home_path(h, &path);
+        if (r < 0) {
+                log_warning_errno(r, "Failed to generate home bus path, ignoring: %m");
+                return 0;
+        }
+
+        if (h->announced)
+                r = sd_bus_emit_properties_changed_strv(h->manager->bus, path, "org.freedesktop.home1.Home", NULL);
+        else
+                r = sd_bus_emit_object_added(h->manager->bus, path);
+        if (r < 0)
+                log_warning_errno(r, "Failed to send home change event, ignoring: %m");
+        else
+                h->announced = true;
+
+        return 0;
+}
+
+int bus_home_emit_change(Home *h) {
+        int r;
+
+        assert(h);
+
+        if (h->deferred_change_event_source)
+                return 1;
+
+        if (!h->manager->event)
+                return 0;
+
+        if (IN_SET(sd_event_get_state(h->manager->event), SD_EVENT_FINISHED, SD_EVENT_EXITING))
+                return 0;
+
+        r = sd_event_add_defer(h->manager->event, &h->deferred_change_event_source, on_deferred_change, h);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate deferred change event source: %m");
+
+        r = sd_event_source_set_priority(h->deferred_change_event_source, SD_EVENT_PRIORITY_IDLE+5);
+        if (r < 0)
+                log_warning_errno(r, "Failed to tweak priority of event source, ignoring: %m");
+
+        (void) sd_event_source_set_description(h->deferred_change_event_source, "deferred-change-event");
+        return 1;
+}
+
+int bus_home_emit_remove(Home *h) {
+        _cleanup_free_ char *path = NULL;
+        int r;
+
+        assert(h);
+
+        if (!h->announced)
+                return 0;
+
+        r = bus_home_path(h, &path);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_emit_object_removed(h->manager->bus, path);
+        if (r < 0)
+                return r;
+
+        h->announced = false;
+        return 1;
+}
diff --git a/src/home/homed-home-bus.h b/src/home/homed-home-bus.h
new file mode 100644 (file)
index 0000000..20516b1
--- /dev/null
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+#include "homed-home.h"
+
+int bus_home_client_is_trusted(Home *h, sd_bus_message *message);
+int bus_home_get_record_json(Home *h, sd_bus_message *message, char **ret, bool *ret_incomplete);
+
+int bus_home_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_deactivate(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_unregister(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_realize(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_fixate(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_authenticate(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_update(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_update_record(Home *home, sd_bus_message *message, UserRecord *hr, sd_bus_error *error);
+int bus_home_method_resize(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_change_password(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_unlock(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_home_method_acquire(sd_bus_message *message, void *userdata, sd_bus_error *error);
+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[];
+
+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);
diff --git a/src/home/homed-home.c b/src/home/homed-home.c
new file mode 100644 (file)
index 0000000..f50de26
--- /dev/null
@@ -0,0 +1,2712 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#if HAVE_LINUX_MEMFD_H
+#include <linux/memfd.h>
+#endif
+
+#include <sys/mman.h>
+#include <sys/quota.h>
+#include <sys/vfs.h>
+
+#include "blockdev-util.h"
+#include "btrfs-util.h"
+#include "bus-common-errors.h"
+#include "env-util.h"
+#include "errno-list.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "home-util.h"
+#include "homed-home-bus.h"
+#include "homed-home.h"
+#include "missing_syscall.h"
+#include "mkdir.h"
+#include "path-util.h"
+#include "process-util.h"
+#include "pwquality-util.h"
+#include "quota-util.h"
+#include "resize-fs.h"
+#include "set.h"
+#include "signal-util.h"
+#include "stat-util.h"
+#include "string-table.h"
+#include "strv.h"
+#include "user-record-sign.h"
+#include "user-record-util.h"
+#include "user-record.h"
+#include "user-util.h"
+
+#define HOME_USERS_MAX 500
+#define PENDING_OPERATIONS_MAX 100
+
+assert_cc(HOME_UID_MIN <= HOME_UID_MAX);
+assert_cc(HOME_USERS_MAX <= (HOME_UID_MAX - HOME_UID_MIN + 1));
+
+static int home_start_work(Home *h, const char *verb, UserRecord *hr, UserRecord *secret);
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(operation_hash_ops, void, trivial_hash_func, trivial_compare_func, Operation, operation_unref);
+
+static int suitable_home_record(UserRecord *hr) {
+        int r;
+
+        assert(hr);
+
+        if (!hr->user_name)
+                return -EUNATCH;
+
+        /* We are a bit more restrictive with what we accept as homed-managed user than what we accept in
+         * home records in general. Let's enforce the stricter rule here. */
+        if (!suitable_user_name(hr->user_name))
+                return -EINVAL;
+        if (!uid_is_valid(hr->uid))
+                return -EINVAL;
+
+        /* Insist we are outside of the dynamic and system range */
+        if (uid_is_system(hr->uid) || gid_is_system(user_record_gid(hr)) ||
+            uid_is_dynamic(hr->uid) || gid_is_dynamic(user_record_gid(hr)))
+                return -EADDRNOTAVAIL;
+
+        /* Insist that GID and UID match */
+        if (user_record_gid(hr) != (gid_t) hr->uid)
+                return -EBADSLT;
+
+        /* Similar for the realm */
+        if (hr->realm) {
+                r = suitable_realm(hr->realm);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        return -EINVAL;
+        }
+
+        return 0;
+}
+
+int home_new(Manager *m, UserRecord *hr, const char *sysfs, Home **ret) {
+        _cleanup_(home_freep) Home *home = NULL;
+        _cleanup_free_ char *nm = NULL, *ns = NULL;
+        int r;
+
+        assert(m);
+        assert(hr);
+
+        r = suitable_home_record(hr);
+        if (r < 0)
+                return r;
+
+        if (hashmap_contains(m->homes_by_name, hr->user_name))
+                return -EBUSY;
+
+        if (hashmap_contains(m->homes_by_uid, UID_TO_PTR(hr->uid)))
+                return -EBUSY;
+
+        if (sysfs && hashmap_contains(m->homes_by_sysfs, sysfs))
+                return -EBUSY;
+
+        if (hashmap_size(m->homes_by_name) >= HOME_USERS_MAX)
+                return -EUSERS;
+
+        nm = strdup(hr->user_name);
+        if (!nm)
+                return -ENOMEM;
+
+        if (sysfs) {
+                ns = strdup(sysfs);
+                if (!ns)
+                        return -ENOMEM;
+        }
+
+        home = new(Home, 1);
+        if (!home)
+                return -ENOMEM;
+
+        *home = (Home) {
+                .manager = m,
+                .user_name = TAKE_PTR(nm),
+                .uid = hr->uid,
+                .state = _HOME_STATE_INVALID,
+                .worker_stdout_fd = -1,
+                .sysfs = TAKE_PTR(ns),
+                .signed_locally = -1,
+        };
+
+        r = hashmap_put(m->homes_by_name, home->user_name, home);
+        if (r < 0)
+                return r;
+
+        r = hashmap_put(m->homes_by_uid, UID_TO_PTR(home->uid), home);
+        if (r < 0)
+                return r;
+
+        if (home->sysfs) {
+                r = hashmap_put(m->homes_by_sysfs, home->sysfs, home);
+                if (r < 0)
+                        return r;
+        }
+
+        r = user_record_clone(hr, USER_RECORD_LOAD_MASK_SECRET, &home->record);
+        if (r < 0)
+                return r;
+
+        (void) bus_manager_emit_auto_login_changed(m);
+        (void) bus_home_emit_change(home);
+
+        if (ret)
+                *ret = TAKE_PTR(home);
+        else
+                TAKE_PTR(home);
+
+        return 0;
+}
+
+Home *home_free(Home *h) {
+
+        if (!h)
+                return NULL;
+
+        if (h->manager) {
+                (void) bus_home_emit_remove(h);
+                (void) bus_manager_emit_auto_login_changed(h->manager);
+
+                if (h->user_name)
+                        (void) hashmap_remove_value(h->manager->homes_by_name, h->user_name, h);
+
+                if (uid_is_valid(h->uid))
+                        (void) hashmap_remove_value(h->manager->homes_by_uid, UID_TO_PTR(h->uid), h);
+
+                if (h->sysfs)
+                        (void) hashmap_remove_value(h->manager->homes_by_sysfs, h->sysfs, h);
+
+                if (h->worker_pid > 0)
+                        (void) hashmap_remove_value(h->manager->homes_by_worker_pid, PID_TO_PTR(h->worker_pid), h);
+
+                if (h->manager->gc_focus == h)
+                        h->manager->gc_focus = NULL;
+        }
+
+        user_record_unref(h->record);
+        user_record_unref(h->secret);
+
+        h->worker_event_source = sd_event_source_unref(h->worker_event_source);
+        safe_close(h->worker_stdout_fd);
+        free(h->user_name);
+        free(h->sysfs);
+
+        h->ref_event_source_please_suspend = sd_event_source_unref(h->ref_event_source_please_suspend);
+        h->ref_event_source_dont_suspend = sd_event_source_unref(h->ref_event_source_dont_suspend);
+
+        h->pending_operations = ordered_set_free(h->pending_operations);
+        h->pending_event_source = sd_event_source_unref(h->pending_event_source);
+        h->deferred_change_event_source = sd_event_source_unref(h->deferred_change_event_source);
+
+        h->current_operation = operation_unref(h->current_operation);
+
+        return mfree(h);
+}
+
+int home_set_record(Home *h, UserRecord *hr) {
+        _cleanup_(user_record_unrefp) UserRecord *new_hr = NULL;
+        Home *other;
+        int r;
+
+        assert(h);
+        assert(h->user_name);
+        assert(h->record);
+        assert(hr);
+
+        if (user_record_equal(h->record, hr))
+                return 0;
+
+        r = suitable_home_record(hr);
+        if (r < 0)
+                return r;
+
+        if (!user_record_compatible(h->record, hr))
+                return -EREMCHG;
+
+        if (!FLAGS_SET(hr->mask, USER_RECORD_REGULAR) ||
+            FLAGS_SET(hr->mask, USER_RECORD_SECRET))
+                return -EINVAL;
+
+        if (FLAGS_SET(h->record->mask, USER_RECORD_STATUS)) {
+                _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+                /* Hmm, the existing record has status fields? If so, copy them over */
+
+                v = json_variant_ref(hr->json);
+                r = json_variant_set_field(&v, "status", json_variant_by_key(h->record->json, "status"));
+                if (r < 0)
+                        return r;
+
+                new_hr = user_record_new();
+                if (!new_hr)
+                        return -ENOMEM;
+
+                r = user_record_load(new_hr, v, USER_RECORD_LOAD_REFUSE_SECRET);
+                if (r < 0)
+                        return r;
+
+                hr = new_hr;
+        }
+
+        other = hashmap_get(h->manager->homes_by_uid, UID_TO_PTR(hr->uid));
+        if (other && other != h)
+                return -EBUSY;
+
+        if (h->uid != hr->uid) {
+                r = hashmap_remove_and_replace(h->manager->homes_by_uid, UID_TO_PTR(h->uid), UID_TO_PTR(hr->uid), h);
+                if (r < 0)
+                        return r;
+        }
+
+        user_record_unref(h->record);
+        h->record = user_record_ref(hr);
+        h->uid = h->record->uid;
+
+        /* The updated record might have a different autologin setting, trigger a PropertiesChanged event for it */
+        (void) bus_manager_emit_auto_login_changed(h->manager);
+        (void) bus_home_emit_change(h);
+
+        return 0;
+}
+
+int home_save_record(Home *h) {
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        _cleanup_free_ char *text = NULL;
+        const char *fn;
+        int r;
+
+        assert(h);
+
+        v = json_variant_ref(h->record->json);
+        r = json_variant_normalize(&v);
+        if (r < 0)
+                log_warning_errno(r, "User record could not be normalized.");
+
+        r = json_variant_format(v, JSON_FORMAT_PRETTY|JSON_FORMAT_NEWLINE, &text);
+        if (r < 0)
+                return r;
+
+        (void) mkdir("/var/lib/systemd/", 0755);
+        (void) mkdir("/var/lib/systemd/home/", 0700);
+
+        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);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+int home_unlink_record(Home *h) {
+        const char *fn;
+
+        assert(h);
+
+        fn = strjoina("/var/lib/systemd/home/", h->user_name, ".identity");
+        if (unlink(fn) < 0 && errno != ENOENT)
+                return -errno;
+
+        fn = strjoina("/run/systemd/home/", h->user_name, ".ref");
+        if (unlink(fn) < 0 && errno != ENOENT)
+                return -errno;
+
+        return 0;
+}
+
+static void home_set_state(Home *h, HomeState state) {
+        HomeState old_state, new_state;
+
+        assert(h);
+
+        old_state = home_get_state(h);
+        h->state = state;
+        new_state = home_get_state(h); /* Query the new state, since the 'state' variable might be set to -1,
+                                        * in which case we synthesize an high-level state on demand */
+
+        log_info("%s: changing state %s → %s", h->user_name,
+                 home_state_to_string(old_state),
+                 home_state_to_string(new_state));
+
+        if (HOME_STATE_IS_EXECUTING_OPERATION(old_state) && !HOME_STATE_IS_EXECUTING_OPERATION(new_state)) {
+                /* If we just finished executing some operation, process the queue of pending operations. And
+                 * enqueue it for GC too. */
+
+                home_schedule_operation(h, NULL, NULL);
+                manager_enqueue_gc(h->manager, h);
+        }
+}
+
+static int home_parse_worker_stdout(int _fd, UserRecord **ret) {
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        _cleanup_close_ int fd = _fd; /* take possession, even on failure */
+        _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        unsigned line, column;
+        struct stat st;
+        int r;
+
+        if (fstat(fd, &st) < 0)
+                return log_error_errno(errno, "Failed to stat stdout fd: %m");
+
+        assert(S_ISREG(st.st_mode));
+
+        if (st.st_size == 0) { /* empty record */
+                *ret = NULL;
+                return 0;
+        }
+
+        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");
+        if (!f)
+                return log_error_errno(errno, "Failed to reopen memfd: %m");
+
+        TAKE_FD(fd);
+
+        if (DEBUG_LOGGING) {
+                _cleanup_free_ char *text = NULL;
+
+                r = read_full_stream(f, &text, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to read from client: %m");
+
+                log_debug("Got from worker: %s", text);
+                rewind(f);
+        }
+
+        r = json_parse_file(f, "stdout", JSON_PARSE_SENSITIVE, &v, &line, &column);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column);
+
+        hr = user_record_new();
+        if (!hr)
+                return log_oom();
+
+        r = user_record_load(hr, v, USER_RECORD_LOAD_REFUSE_SECRET);
+        if (r < 0)
+                return log_error_errno(r, "Failed to load home record identity: %m");
+
+        *ret = TAKE_PTR(hr);
+        return 1;
+}
+
+static int home_verify_user_record(Home *h, UserRecord *hr, bool *ret_signed_locally, sd_bus_error *ret_error) {
+        int is_signed;
+
+        assert(h);
+        assert(hr);
+        assert(ret_signed_locally);
+
+        is_signed = manager_verify_user_record(h->manager, hr);
+        switch (is_signed) {
+
+        case USER_RECORD_SIGNED_EXCLUSIVE:
+                log_info("Home %s is signed exclusively by our key, accepting.", hr->user_name);
+                *ret_signed_locally = true;
+                return 0;
+
+        case USER_RECORD_SIGNED:
+                log_info("Home %s is signed by our key (and others), accepting.", hr->user_name);
+                *ret_signed_locally = false;
+                return 0;
+
+        case USER_RECORD_FOREIGN:
+                log_info("Home %s is signed by foreign key we like, accepting.", hr->user_name);
+                *ret_signed_locally = false;
+                return 0;
+
+        case USER_RECORD_UNSIGNED:
+                sd_bus_error_setf(ret_error, BUS_ERROR_BAD_SIGNATURE, "User record %s is not signed at all, refusing.", hr->user_name);
+                return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Home %s contains user record that is not signed at all, refusing.", hr->user_name);
+
+        case -ENOKEY:
+                sd_bus_error_setf(ret_error, BUS_ERROR_BAD_SIGNATURE, "User record %s is not signed by any known key, refusing.", hr->user_name);
+                return log_error_errno(is_signed, "Home %s contians user record that is not signed by any known key, refusing.", hr->user_name);
+
+        default:
+                assert(is_signed < 0);
+                return log_error_errno(is_signed, "Failed to verify signature on user record for %s, refusing fixation: %m", hr->user_name);
+        }
+}
+
+static int convert_worker_errno(Home *h, int e, sd_bus_error *error) {
+        /* Converts the error numbers the worker process returned into somewhat sensible dbus errors */
+
+        switch (e) {
+
+        case -EMSGSIZE:
+                return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type cannot shrinked");
+        case -ETXTBSY:
+                return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type can only be shrinked offline");
+        case -ERANGE:
+                return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File system size too small");
+        case -ENOLINK:
+                return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "System does not support selected storage backend");
+        case -EPROTONOSUPPORT:
+                return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "System does not support selected file system");
+        case -ENOTTY:
+                return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Operation not supported on storage backend");
+        case -ESOCKTNOSUPPORT:
+                return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Operation not supported on file system");
+        case -ENOKEY:
+                return sd_bus_error_setf(error, BUS_ERROR_BAD_PASSWORD, "Password for home %s is incorrect or not sufficient for authentication.", h->user_name);
+        case -EBADSLT:
+                return sd_bus_error_setf(error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN, "Password for home %s is incorrect or not sufficient, and configured security token not found either.", h->user_name);
+        case -ENOANO:
+                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 -EOWNERDEAD:
+                return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PIN_LOCKED, "PIN of security token locked.");
+        case -ENOLCK:
+                return sd_bus_error_setf(error, BUS_ERROR_TOKEN_BAD_PIN, "Bad PIN of security token.");
+        case -ETOOMANYREFS:
+                return sd_bus_error_setf(error, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT, "Bad PIN of security token, and only a few tries left.");
+        case -EUCLEAN:
+                return sd_bus_error_setf(error, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT, "Bad PIN of security token, and only one try left.");
+        case -EBUSY:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
+        case -ENOEXEC:
+                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);
+        }
+
+        return 0;
+}
+
+static void home_count_bad_authentication(Home *h, bool save) {
+        int r;
+
+        assert(h);
+
+        r = user_record_bad_authentication(h->record);
+        if (r < 0) {
+                log_warning_errno(r, "Failed to increase bad authentication counter, ignoring: %m");
+                return;
+        }
+
+        if (save) {
+                r = home_save_record(h);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to write home record to disk, ignoring: %m");
+        }
+}
+
+static void home_fixate_finish(Home *h, int ret, UserRecord *hr) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(user_record_unrefp) UserRecord *secret = NULL;
+        bool signed_locally;
+        int r;
+
+        assert(h);
+        assert(IN_SET(h->state, HOME_FIXATING, HOME_FIXATING_FOR_ACTIVATION, HOME_FIXATING_FOR_ACQUIRE));
+
+        secret = TAKE_PTR(h->secret); /* Take possession */
+
+        if (ret < 0) {
+                if (ret == -ENOKEY)
+                        (void) home_count_bad_authentication(h, false);
+
+                (void) convert_worker_errno(h, ret, &error);
+                r = log_error_errno(ret, "Fixation failed: %m");
+                goto fail;
+        }
+        if (!hr) {
+                r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Did not receive user record from worker process, fixation failed.");
+                goto fail;
+        }
+
+        r = home_verify_user_record(h, hr, &signed_locally, &error);
+        if (r < 0)
+                goto fail;
+
+        r = home_set_record(h, hr);
+        if (r < 0) {
+                log_error_errno(r, "Failed to update home record: %m");
+                goto fail;
+        }
+
+        h->signed_locally = signed_locally;
+
+        /* When we finished fixating (and don't follow-up with activation), let's count this as good authentication */
+        if (h->state == HOME_FIXATING) {
+                r = user_record_good_authentication(h->record);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to increase good authentication counter, ignoring: %m");
+        }
+
+        r = home_save_record(h);
+        if (r < 0)
+                log_warning_errno(r, "Failed to write home record to disk, ignoring: %m");
+
+        if (IN_SET(h->state, HOME_FIXATING_FOR_ACTIVATION, HOME_FIXATING_FOR_ACQUIRE)) {
+
+                r = home_start_work(h, "activate", h->record, secret);
+                if (r < 0) {
+                        h->current_operation = operation_result_unref(h->current_operation, r, NULL);
+                        home_set_state(h, _HOME_STATE_INVALID);
+                } else
+                        home_set_state(h, h->state == HOME_FIXATING_FOR_ACTIVATION ? HOME_ACTIVATING : HOME_ACTIVATING_FOR_ACQUIRE);
+
+                return;
+        }
+
+        log_debug("Fixation of %s completed.", h->user_name);
+
+        h->current_operation = operation_result_unref(h->current_operation, 0, NULL);
+
+        /* Reset the state to "invalid", which makes home_get_state() test if the image exists and returns
+         * HOME_ABSENT vs. HOME_INACTIVE as necessary. */
+        home_set_state(h, _HOME_STATE_INVALID);
+        return;
+
+fail:
+        /* If fixation fails, we stay in unfixated state! */
+        h->current_operation = operation_result_unref(h->current_operation, r, &error);
+        home_set_state(h, HOME_UNFIXATED);
+}
+
+static void home_activate_finish(Home *h, int ret, UserRecord *hr) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        int r;
+
+        assert(h);
+        assert(IN_SET(h->state, HOME_ACTIVATING, HOME_ACTIVATING_FOR_ACQUIRE));
+
+        if (ret < 0) {
+                if (ret == -ENOKEY)
+                        home_count_bad_authentication(h, true);
+
+                (void) convert_worker_errno(h, ret, &error);
+                r = log_error_errno(ret, "Activation failed: %m");
+                goto finish;
+        }
+
+        if (hr) {
+                bool signed_locally;
+
+                r = home_verify_user_record(h, hr, &signed_locally, &error);
+                if (r < 0)
+                        goto finish;
+
+                r = home_set_record(h, hr);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to update home record, ignoring: %m");
+                        goto finish;
+                }
+
+                h->signed_locally = signed_locally;
+
+                r = user_record_good_authentication(h->record);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to increase good authentication counter, ignoring: %m");
+
+                r = home_save_record(h);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to write home record to disk, ignoring: %m");
+        }
+
+        log_debug("Activation of %s completed.", h->user_name);
+        r = 0;
+
+finish:
+        h->current_operation = operation_result_unref(h->current_operation, r, &error);
+        home_set_state(h, _HOME_STATE_INVALID);
+}
+
+static void home_deactivate_finish(Home *h, int ret, UserRecord *hr) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        int r;
+
+        assert(h);
+        assert(h->state == HOME_DEACTIVATING);
+        assert(!hr); /* We don't expect a record on this operation */
+
+        if (ret < 0) {
+                (void) convert_worker_errno(h, ret, &error);
+                r = log_error_errno(ret, "Deactivation of %s failed: %m", h->user_name);
+                goto finish;
+        }
+
+        log_debug("Deactivation of %s completed.", h->user_name);
+        r = 0;
+
+finish:
+        h->current_operation = operation_result_unref(h->current_operation, r, &error);
+        home_set_state(h, _HOME_STATE_INVALID);
+}
+
+static void home_remove_finish(Home *h, int ret, UserRecord *hr) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        Manager *m;
+        int r;
+
+        assert(h);
+        assert(h->state == HOME_REMOVING);
+        assert(!hr); /* We don't expect a record on this operation */
+
+        m = h->manager;
+
+        if (ret < 0 && ret != -EALREADY) {
+                (void) convert_worker_errno(h, ret, &error);
+                r = log_error_errno(ret, "Removing %s failed: %m", h->user_name);
+                goto fail;
+        }
+
+        /* For a couple of storage types we can't delete the actual data storage when called (such as LUKS on
+         * partitions like USB sticks, or so). Sometimes these storage locations are among those we normally
+         * automatically discover in /home or in udev. When such a home is deleted let's hence issue a rescan
+         * after completion, so that "unfixated" entries are rediscovered.  */
+        if (!IN_SET(user_record_test_image_path(h->record), USER_TEST_UNDEFINED, USER_TEST_ABSENT))
+                manager_enqueue_rescan(m);
+
+        /* The image is now removed from disk. Now also remove our stored record */
+        r = home_unlink_record(h);
+        if (r < 0) {
+                log_error_errno(r, "Removing record file failed: %m");
+                goto fail;
+        }
+
+        log_debug("Removal of %s completed.", h->user_name);
+        h->current_operation = operation_result_unref(h->current_operation, 0, NULL);
+
+        /* Unload this record from memory too now. */
+        h = home_free(h);
+        return;
+
+fail:
+        h->current_operation = operation_result_unref(h->current_operation, r, &error);
+        home_set_state(h, _HOME_STATE_INVALID);
+}
+
+static void home_create_finish(Home *h, int ret, UserRecord *hr) {
+        int r;
+
+        assert(h);
+        assert(h->state == HOME_CREATING);
+
+        if (ret < 0) {
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+
+                (void) convert_worker_errno(h, ret, &error);
+                log_error_errno(ret, "Operation on %s failed: %m", h->user_name);
+                h->current_operation = operation_result_unref(h->current_operation, ret, &error);
+
+                if (h->unregister_on_failure) {
+                        (void) home_unlink_record(h);
+                        h = home_free(h);
+                        return;
+                }
+
+                home_set_state(h, _HOME_STATE_INVALID);
+                return;
+        }
+
+        if (hr) {
+                r = home_set_record(h, hr);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to update home record, ignoring: %m");
+        }
+
+        r = home_save_record(h);
+        if (r < 0)
+                log_warning_errno(r, "Failed to save record to disk, ignoring: %m");
+
+        log_debug("Creation of %s completed.", h->user_name);
+
+        h->current_operation = operation_result_unref(h->current_operation, 0, NULL);
+        home_set_state(h, _HOME_STATE_INVALID);
+}
+
+static void home_change_finish(Home *h, int ret, UserRecord *hr) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        int r;
+
+        assert(h);
+
+        if (ret < 0) {
+                if (ret == -ENOKEY)
+                        (void) home_count_bad_authentication(h, true);
+
+                (void) convert_worker_errno(h, ret, &error);
+                r = log_error_errno(ret, "Change operation failed: %m");
+                goto finish;
+        }
+
+        if (hr) {
+                r = home_set_record(h, hr);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to update home record, ignoring: %m");
+                else {
+                        r = user_record_good_authentication(h->record);
+                        if (r < 0)
+                                log_warning_errno(r, "Failed to increase good authentication counter, ignoring: %m");
+
+                        r = home_save_record(h);
+                        if (r < 0)
+                                log_warning_errno(r, "Failed to write home record to disk, ignoring: %m");
+                }
+        }
+
+        log_debug("Change operation of %s completed.", h->user_name);
+        r = 0;
+
+finish:
+        h->current_operation = operation_result_unref(h->current_operation, r, &error);
+        home_set_state(h, _HOME_STATE_INVALID);
+}
+
+static void home_locking_finish(Home *h, int ret, UserRecord *hr) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        int r;
+
+        assert(h);
+        assert(h->state == HOME_LOCKING);
+
+        if (ret < 0) {
+                (void) convert_worker_errno(h, ret, &error);
+                r = log_error_errno(ret, "Locking operation failed: %m");
+                goto finish;
+        }
+
+        log_debug("Locking operation of %s completed.", h->user_name);
+        h->current_operation = operation_result_unref(h->current_operation, 0, NULL);
+        home_set_state(h, HOME_LOCKED);
+        return;
+
+finish:
+        /* If a specific home doesn't know the concept of locking, then that's totally OK, don't propagate
+         * the error if we are executing a LockAllHomes() operation. */
+
+        if (h->current_operation->type == OPERATION_LOCK_ALL && r == -ENOTTY)
+                h->current_operation = operation_result_unref(h->current_operation, 0, NULL);
+        else
+                h->current_operation = operation_result_unref(h->current_operation, r, &error);
+
+        home_set_state(h, _HOME_STATE_INVALID);
+}
+
+static void home_unlocking_finish(Home *h, int ret, UserRecord *hr) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        int r;
+
+        assert(h);
+        assert(IN_SET(h->state, HOME_UNLOCKING, HOME_UNLOCKING_FOR_ACQUIRE));
+
+        if (ret < 0) {
+                if (ret == -ENOKEY)
+                        (void) home_count_bad_authentication(h, true);
+
+                (void) convert_worker_errno(h, ret, &error);
+                r = log_error_errno(ret, "Unlocking operation failed: %m");
+
+                /* Revert to locked state */
+                home_set_state(h, HOME_LOCKED);
+                h->current_operation = operation_result_unref(h->current_operation, r, &error);
+                return;
+        }
+
+        r = user_record_good_authentication(h->record);
+        if (r < 0)
+                log_warning_errno(r, "Failed to increase good authentication counter, ignoring: %m");
+        else {
+                r = home_save_record(h);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to write home record to disk, ignoring: %m");
+        }
+
+        log_debug("Unlocking operation of %s completed.", h->user_name);
+
+        h->current_operation = operation_result_unref(h->current_operation, r, &error);
+        home_set_state(h, _HOME_STATE_INVALID);
+        return;
+}
+
+static void home_authenticating_finish(Home *h, int ret, UserRecord *hr) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        int r;
+
+        assert(h);
+        assert(IN_SET(h->state, HOME_AUTHENTICATING, HOME_AUTHENTICATING_WHILE_ACTIVE, HOME_AUTHENTICATING_FOR_ACQUIRE));
+
+        if (ret < 0) {
+                if (ret == -ENOKEY)
+                        (void) home_count_bad_authentication(h, true);
+
+                (void) convert_worker_errno(h, ret, &error);
+                r = log_error_errno(ret, "Authentication failed: %m");
+                goto finish;
+        }
+
+        if (hr) {
+                r = home_set_record(h, hr);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to update home record, ignoring: %m");
+                else {
+                        r = user_record_good_authentication(h->record);
+                        if (r < 0)
+                                log_warning_errno(r, "Failed to increase good authentication counter, ignoring: %m");
+
+                        r = home_save_record(h);
+                        if (r < 0)
+                                log_warning_errno(r, "Failed to write home record to disk, ignoring: %m");
+                }
+        }
+
+        log_debug("Authentication of %s completed.", h->user_name);
+        r = 0;
+
+finish:
+        h->current_operation = operation_result_unref(h->current_operation, r, &error);
+        home_set_state(h, _HOME_STATE_INVALID);
+}
+
+static int home_on_worker_process(sd_event_source *s, const siginfo_t *si, void *userdata) {
+        _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+        Home *h = userdata;
+        int ret;
+
+        assert(s);
+        assert(si);
+        assert(h);
+
+        assert(h->worker_pid == si->si_pid);
+        assert(h->worker_event_source);
+        assert(h->worker_stdout_fd >= 0);
+
+        (void) hashmap_remove_value(h->manager->homes_by_worker_pid, PID_TO_PTR(h->worker_pid), h);
+
+        h->worker_pid = 0;
+        h->worker_event_source = sd_event_source_unref(h->worker_event_source);
+
+        if (si->si_code != CLD_EXITED) {
+                assert(IN_SET(si->si_code, CLD_KILLED, CLD_DUMPED));
+                ret = log_debug_errno(SYNTHETIC_ERRNO(EPROTO), "Worker process died abnormally with signal %s.", signal_to_string(si->si_status));
+        } else if (si->si_status != EXIT_SUCCESS) {
+                /* If we received an error code via sd_notify(), use it */
+                if (h->worker_error_code != 0)
+                        ret = log_debug_errno(h->worker_error_code, "Worker reported error code %s.", errno_to_name(h->worker_error_code));
+                else
+                        ret = log_debug_errno(SYNTHETIC_ERRNO(EPROTO), "Worker exited with exit code %i.", si->si_status);
+        } else
+                ret = home_parse_worker_stdout(TAKE_FD(h->worker_stdout_fd), &hr);
+
+        h->worker_stdout_fd = safe_close(h->worker_stdout_fd);
+
+        switch (h->state) {
+
+        case HOME_FIXATING:
+        case HOME_FIXATING_FOR_ACTIVATION:
+        case HOME_FIXATING_FOR_ACQUIRE:
+                home_fixate_finish(h, ret, hr);
+                break;
+
+        case HOME_ACTIVATING:
+        case HOME_ACTIVATING_FOR_ACQUIRE:
+                home_activate_finish(h, ret, hr);
+                break;
+
+        case HOME_DEACTIVATING:
+                home_deactivate_finish(h, ret, hr);
+                break;
+
+        case HOME_LOCKING:
+                home_locking_finish(h, ret, hr);
+                break;
+
+        case HOME_UNLOCKING:
+        case HOME_UNLOCKING_FOR_ACQUIRE:
+                home_unlocking_finish(h, ret, hr);
+                break;
+
+        case HOME_CREATING:
+                home_create_finish(h, ret, hr);
+                break;
+
+        case HOME_REMOVING:
+                home_remove_finish(h, ret, hr);
+                break;
+
+        case HOME_UPDATING:
+        case HOME_UPDATING_WHILE_ACTIVE:
+        case HOME_RESIZING:
+        case HOME_RESIZING_WHILE_ACTIVE:
+        case HOME_PASSWD:
+        case HOME_PASSWD_WHILE_ACTIVE:
+                home_change_finish(h, ret, hr);
+                break;
+
+        case HOME_AUTHENTICATING:
+        case HOME_AUTHENTICATING_WHILE_ACTIVE:
+        case HOME_AUTHENTICATING_FOR_ACQUIRE:
+                home_authenticating_finish(h, ret, hr);
+                break;
+
+        default:
+                assert_not_reached("Unexpected state after worker exited");
+        }
+
+        return 0;
+}
+
+static int home_start_work(Home *h, const char *verb, UserRecord *hr, UserRecord *secret) {
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        _cleanup_(erase_and_freep) char *formatted = NULL;
+        _cleanup_close_ int stdin_fd = -1, stdout_fd = -1;
+        pid_t pid = 0;
+        int r;
+
+        assert(h);
+        assert(verb);
+        assert(hr);
+
+        if (h->worker_pid != 0)
+                return -EBUSY;
+
+        assert(h->worker_stdout_fd < 0);
+        assert(!h->worker_event_source);
+
+        v = json_variant_ref(hr->json);
+
+        if (secret) {
+                JsonVariant *sub = NULL;
+
+                sub = json_variant_by_key(secret->json, "secret");
+                if (!sub)
+                        return -ENOKEY;
+
+                r = json_variant_set_field(&v, "secret", sub);
+                if (r < 0)
+                        return r;
+        }
+
+        r = json_variant_format(v, 0, &formatted);
+        if (r < 0)
+                return r;
+
+        stdin_fd = acquire_data_fd(formatted, strlen(formatted), 0);
+        if (stdin_fd < 0)
+                return stdin_fd;
+
+        log_debug("Sending to worker: %s", formatted);
+
+        stdout_fd = memfd_create("homework-stdout", MFD_CLOEXEC);
+        if (stdout_fd < 0)
+                return -errno;
+
+        r = safe_fork_full("(sd-homework)",
+                           (int[]) { stdin_fd, stdout_fd }, 2,
+                           FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG, &pid);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                /* Child */
+
+                if (setenv("NOTIFY_SOCKET", "/run/systemd/home/notify", 1) < 0) {
+                        log_error_errno(errno, "Failed to set $NOTIFY_SOCKET: %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");
+                        _exit(EXIT_FAILURE);
+                }
+
+                stdin_fd = stdout_fd = -1; /* have been invalidated by rearrange_stdio() */
+
+                execl(SYSTEMD_HOMEWORK_PATH, SYSTEMD_HOMEWORK_PATH, verb, NULL);
+                log_error_errno(errno, "Failed to invoke " SYSTEMD_HOMEWORK_PATH ": %m");
+                _exit(EXIT_FAILURE);
+        }
+
+        r = sd_event_add_child(h->manager->event, &h->worker_event_source, pid, WEXITED, home_on_worker_process, h);
+        if (r < 0)
+                return r;
+
+        (void) sd_event_source_set_description(h->worker_event_source, "worker");
+
+        r = hashmap_put(h->manager->homes_by_worker_pid, PID_TO_PTR(pid), h);
+        if (r < 0) {
+                h->worker_event_source = sd_event_source_unref(h->worker_event_source);
+                return r;
+        }
+
+        h->worker_stdout_fd = TAKE_FD(stdout_fd);
+        h->worker_pid = pid;
+        h->worker_error_code = 0;
+
+        return 0;
+}
+
+static int home_ratelimit(Home *h, sd_bus_error *error) {
+        int r, ret;
+
+        assert(h);
+
+        ret = user_record_ratelimit(h->record);
+        if (ret < 0)
+                return ret;
+
+        if (h->state != HOME_UNFIXATED) {
+                r = home_save_record(h);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to save updated record, ignoring: %m");
+        }
+
+        if (ret == 0) {
+                char buf[FORMAT_TIMESPAN_MAX];
+                usec_t t, n;
+
+                n = now(CLOCK_REALTIME);
+                t = user_record_ratelimit_next_try(h->record);
+
+                if (t != USEC_INFINITY && t > n)
+                        return sd_bus_error_setf(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT, "Too many login attempts, please try again in %s!",
+                                                 format_timespan(buf, sizeof(buf), t - n, USEC_PER_SEC));
+
+                return sd_bus_error_setf(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT, "Too many login attempts, please try again later.");
+        }
+
+        return 0;
+}
+
+static int home_fixate_internal(
+                Home *h,
+                UserRecord *secret,
+                HomeState for_state,
+                sd_bus_error *error) {
+
+        int r;
+
+        assert(h);
+        assert(IN_SET(for_state, HOME_FIXATING, HOME_FIXATING_FOR_ACTIVATION, HOME_FIXATING_FOR_ACQUIRE));
+
+        r = home_start_work(h, "inspect", h->record, secret);
+        if (r < 0)
+                return r;
+
+        if (for_state == HOME_FIXATING_FOR_ACTIVATION) {
+                /* Remember the secret data, since we need it for the activation again, later on. */
+                user_record_unref(h->secret);
+                h->secret = user_record_ref(secret);
+        }
+
+        home_set_state(h, for_state);
+        return 0;
+}
+
+int home_fixate(Home *h, UserRecord *secret, sd_bus_error *error) {
+        int r;
+
+        assert(h);
+
+        switch (home_get_state(h)) {
+        case HOME_ABSENT:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
+        case HOME_INACTIVE:
+        case HOME_ACTIVE:
+        case HOME_LOCKED:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_ALREADY_FIXATED, "Home %s is already fixated.", h->user_name);
+        case HOME_UNFIXATED:
+                break;
+        default:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
+        }
+
+        r = home_ratelimit(h, error);
+        if (r < 0)
+                return r;
+
+        return home_fixate_internal(h, secret, HOME_FIXATING, error);
+}
+
+static int home_activate_internal(Home *h, UserRecord *secret, HomeState for_state, sd_bus_error *error) {
+        int r;
+
+        assert(h);
+        assert(IN_SET(for_state, HOME_ACTIVATING, HOME_ACTIVATING_FOR_ACQUIRE));
+
+        r = home_start_work(h, "activate", h->record, secret);
+        if (r < 0)
+                return r;
+
+        home_set_state(h, for_state);
+        return 0;
+}
+
+int home_activate(Home *h, UserRecord *secret, sd_bus_error *error) {
+        int r;
+
+        assert(h);
+
+        switch (home_get_state(h)) {
+        case HOME_UNFIXATED:
+                return home_fixate_internal(h, secret, HOME_FIXATING_FOR_ACTIVATION, error);
+        case HOME_ABSENT:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
+        case HOME_ACTIVE:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_ALREADY_ACTIVE, "Home %s is already active.", h->user_name);
+        case HOME_LOCKED:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+        case HOME_INACTIVE:
+                break;
+        default:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
+        }
+
+        r = home_ratelimit(h, error);
+        if (r < 0)
+                return r;
+
+        return home_activate_internal(h, secret, HOME_ACTIVATING, error);
+}
+
+static int home_authenticate_internal(Home *h, UserRecord *secret, HomeState for_state, sd_bus_error *error) {
+        int r;
+
+        assert(h);
+        assert(IN_SET(for_state, HOME_AUTHENTICATING, HOME_AUTHENTICATING_WHILE_ACTIVE, HOME_AUTHENTICATING_FOR_ACQUIRE));
+
+        r = home_start_work(h, "inspect", h->record, secret);
+        if (r < 0)
+                return r;
+
+        home_set_state(h, for_state);
+        return 0;
+}
+
+int home_authenticate(Home *h, UserRecord *secret, sd_bus_error *error) {
+        HomeState state;
+        int r;
+
+        assert(h);
+
+        state = home_get_state(h);
+        switch (state) {
+        case HOME_ABSENT:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
+        case HOME_LOCKED:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+        case HOME_UNFIXATED:
+        case HOME_INACTIVE:
+        case HOME_ACTIVE:
+                break;
+        default:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
+        }
+
+        r = home_ratelimit(h, error);
+        if (r < 0)
+                return r;
+
+        return home_authenticate_internal(h, secret, state == HOME_ACTIVE ? HOME_AUTHENTICATING_WHILE_ACTIVE : HOME_AUTHENTICATING, error);
+}
+
+static int home_deactivate_internal(Home *h, bool force, sd_bus_error *error) {
+        int r;
+
+        assert(h);
+
+        r = home_start_work(h, force ? "deactivate-force" : "deactivate", h->record, NULL);
+        if (r < 0)
+                return r;
+
+        home_set_state(h, HOME_DEACTIVATING);
+        return 0;
+}
+
+int home_deactivate(Home *h, bool force, sd_bus_error *error) {
+        assert(h);
+
+        switch (home_get_state(h)) {
+        case HOME_UNFIXATED:
+        case HOME_ABSENT:
+        case HOME_INACTIVE:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_ACTIVE, "Home %s not active.", h->user_name);
+        case HOME_LOCKED:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+        case HOME_ACTIVE:
+                break;
+        default:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
+        }
+
+        return home_deactivate_internal(h, force, error);
+}
+
+int home_create(Home *h, UserRecord *secret, sd_bus_error *error) {
+        int r;
+
+        assert(h);
+
+        switch (home_get_state(h)) {
+        case HOME_INACTIVE:
+                if (h->record->storage < 0)
+                        break; /* if no storage is defined we don't know what precisely to look for, hence
+                                * HOME_INACTIVE is OK in that case too. */
+
+                if (IN_SET(user_record_test_image_path(h->record), USER_TEST_MAYBE, USER_TEST_UNDEFINED))
+                        break; /* And if the image path test isn't conclusive, let's also go on */
+
+                _fallthrough_;
+        case HOME_UNFIXATED:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_EXISTS, "Home of user %s already exists.", h->user_name);
+        case HOME_ABSENT:
+                break;
+        case HOME_ACTIVE:
+        case HOME_LOCKED:
+        default:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
+        }
+
+        if (h->record->enforce_password_policy == false)
+                log_debug("Password quality check turned off for account, skipping.");
+        else {
+                r = quality_check_password(h->record, secret, error);
+                if (r < 0)
+                        return r;
+        }
+
+        r = home_start_work(h, "create", h->record, secret);
+        if (r < 0)
+                return r;
+
+        home_set_state(h, HOME_CREATING);
+        return 0;
+}
+
+int home_remove(Home *h, sd_bus_error *error) {
+        HomeState state;
+        int r;
+
+        assert(h);
+
+        state = home_get_state(h);
+        switch (state) {
+        case HOME_ABSENT: /* If the home directory is absent, then this is just like unregistering */
+                return home_unregister(h, error);
+        case HOME_LOCKED:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+        case HOME_UNFIXATED:
+        case HOME_INACTIVE:
+                break;
+        case HOME_ACTIVE:
+        default:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
+        }
+
+        r = home_start_work(h, "remove", h->record, NULL);
+        if (r < 0)
+                return r;
+
+        home_set_state(h, HOME_REMOVING);
+        return 0;
+}
+
+static int user_record_extend_with_binding(UserRecord *hr, UserRecord *with_binding, UserRecordLoadFlags flags, UserRecord **ret) {
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        _cleanup_(user_record_unrefp) UserRecord *nr = NULL;
+        JsonVariant *binding;
+        int r;
+
+        assert(hr);
+        assert(with_binding);
+        assert(ret);
+
+        assert_se(v = json_variant_ref(hr->json));
+
+        binding = json_variant_by_key(with_binding->json, "binding");
+        if (binding) {
+                r = json_variant_set_field(&v, "binding", binding);
+                if (r < 0)
+                        return r;
+        }
+
+        nr = user_record_new();
+        if (!nr)
+                return -ENOMEM;
+
+        r = user_record_load(nr, v, flags);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(nr);
+        return 0;
+}
+
+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;
+
+        assert(h);
+        assert(verb);
+        assert(hr);
+
+        if (!user_record_compatible(hr, h->record))
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_MISMATCH, "Updated user record is not compatible with existing one.");
+        c = user_record_compare_last_change(hr, h->record); /* refuse downgrades */
+        if (c < 0)
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_DOWNGRADE, "Refusing to update to older home record.");
+
+        if (!secret && FLAGS_SET(hr->mask, USER_RECORD_SECRET)) {
+                r = user_record_clone(hr, USER_RECORD_EXTRACT_SECRET, &saved_secret);
+                if (r < 0)
+                        return r;
+
+                secret = saved_secret;
+        }
+
+        r = manager_verify_user_record(h->manager, hr);
+        switch (r) {
+
+        case USER_RECORD_UNSIGNED:
+                if (h->signed_locally <= 0) /* If the existing record is not owned by us, don't accept an
+                                             * unsigned new record. i.e. only implicitly sign new records
+                                             * that where previously signed by us too. */
+                        return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_SIGNED, "Home %s is signed and cannot be modified locally.", h->user_name);
+
+                /* The updated record is not signed, then do so now */
+                r = manager_sign_user_record(h->manager, hr, &signed_hr, error);
+                if (r < 0)
+                        return r;
+
+                hr = signed_hr;
+                break;
+
+        case USER_RECORD_SIGNED_EXCLUSIVE:
+        case USER_RECORD_SIGNED:
+        case USER_RECORD_FOREIGN:
+                /* Has already been signed. Great! */
+                break;
+
+        case -ENOKEY:
+        default:
+                return r;
+        }
+
+        r = user_record_extend_with_binding(hr, h->record, USER_RECORD_LOAD_MASK_SECRET, &new_hr);
+        if (r < 0)
+                return r;
+
+        if (c == 0) {
+                /* different payload but same lastChangeUSec field? That's not cool! */
+
+                r = user_record_masked_equal(new_hr, h->record, USER_RECORD_REGULAR|USER_RECORD_PRIVILEGED|USER_RECORD_PER_MACHINE);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_MISMATCH, "Home record different but timestamp remained the same, refusing.");
+        }
+
+        r = home_start_work(h, verb, new_hr, secret);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+int home_update(Home *h, UserRecord *hr, sd_bus_error *error) {
+        HomeState state;
+        int r;
+
+        assert(h);
+        assert(hr);
+
+        state = home_get_state(h);
+        switch (state) {
+        case HOME_UNFIXATED:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_UNFIXATED, "Home %s has not been fixated yet.", h->user_name);
+        case HOME_ABSENT:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
+        case HOME_LOCKED:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+        case HOME_INACTIVE:
+        case HOME_ACTIVE:
+                break;
+        default:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
+        }
+
+        r = home_ratelimit(h, error);
+        if (r < 0)
+                return r;
+
+        r = home_update_internal(h, "update", hr, NULL, error);
+        if (r < 0)
+                return r;
+
+        home_set_state(h, state == HOME_ACTIVE ? HOME_UPDATING_WHILE_ACTIVE : HOME_UPDATING);
+        return 0;
+}
+
+int home_resize(Home *h, uint64_t disk_size, UserRecord *secret, sd_bus_error *error) {
+        _cleanup_(user_record_unrefp) UserRecord *c = NULL;
+        HomeState state;
+        int r;
+
+        assert(h);
+
+        state = home_get_state(h);
+        switch (state) {
+        case HOME_UNFIXATED:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_UNFIXATED, "Home %s has not been fixated yet.", h->user_name);
+        case HOME_ABSENT:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
+        case HOME_LOCKED:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+        case HOME_INACTIVE:
+        case HOME_ACTIVE:
+                break;
+        default:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
+        }
+
+        r = home_ratelimit(h, error);
+        if (r < 0)
+                return r;
+
+        if (disk_size == UINT64_MAX || disk_size == h->record->disk_size) {
+                if (h->record->disk_size == UINT64_MAX)
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not disk size to resize to specified.");
+
+                c = user_record_ref(h->record); /* Shortcut if size is unspecified or matches the record */
+        } else {
+                _cleanup_(user_record_unrefp) UserRecord *signed_c = NULL;
+
+                if (h->signed_locally <= 0) /* Don't allow changing of records not signed only by us */
+                        return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_SIGNED, "Home %s is signed and cannot be modified locally.", h->user_name);
+
+                r = user_record_clone(h->record, USER_RECORD_LOAD_REFUSE_SECRET, &c);
+                if (r < 0)
+                        return r;
+
+                r = user_record_set_disk_size(c, disk_size);
+                if (r == -ERANGE)
+                        return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "Requested size for home %s out of acceptable range.", h->user_name);
+                if (r < 0)
+                        return r;
+
+                r = user_record_update_last_changed(c, false);
+                if (r == -ECHRNG)
+                        return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_MISMATCH, "Record last change time of %s is newer than current time, cannot update.", h->user_name);
+                if (r < 0)
+                        return r;
+
+                r = manager_sign_user_record(h->manager, c, &signed_c, error);
+                if (r < 0)
+                        return r;
+
+                user_record_unref(c);
+                c = TAKE_PTR(signed_c);
+        }
+
+        r = home_update_internal(h, "resize", c, secret, error);
+        if (r < 0)
+                return r;
+
+        home_set_state(h, state == HOME_ACTIVE ? HOME_RESIZING_WHILE_ACTIVE : HOME_RESIZING);
+        return 0;
+}
+
+static int home_may_change_password(
+                Home *h,
+                sd_bus_error *error) {
+
+        int r;
+
+        assert(h);
+
+        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 */
+        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)
+                return log_error_errno(r, "Failed to test password expiry: %m");
+
+        return 0; /* not expired */
+}
+
+int home_passwd(Home *h,
+                UserRecord *new_secret,
+                UserRecord *old_secret,
+                sd_bus_error *error) {
+
+        _cleanup_(user_record_unrefp) UserRecord *c = NULL, *merged_secret = NULL, *signed_c = NULL;
+        HomeState state;
+        int r;
+
+        assert(h);
+
+        if (h->signed_locally <= 0) /* Don't allow changing of records not signed only by us */
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_SIGNED, "Home %s is signed and cannot be modified locally.", h->user_name);
+
+        state = home_get_state(h);
+        switch (state) {
+        case HOME_UNFIXATED:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_UNFIXATED, "Home %s has not been fixated yet.", h->user_name);
+        case HOME_ABSENT:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
+        case HOME_LOCKED:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+        case HOME_INACTIVE:
+        case HOME_ACTIVE:
+                break;
+        default:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
+        }
+
+        r = home_ratelimit(h, error);
+        if (r < 0)
+                return r;
+
+        r = home_may_change_password(h, error);
+        if (r < 0)
+                return r;
+
+        r = user_record_clone(h->record, USER_RECORD_LOAD_REFUSE_SECRET, &c);
+        if (r < 0)
+                return r;
+
+        merged_secret = user_record_new();
+        if (!merged_secret)
+                return -ENOMEM;
+
+        r = user_record_merge_secret(merged_secret, old_secret);
+        if (r < 0)
+                return r;
+
+        r = user_record_merge_secret(merged_secret, new_secret);
+        if (r < 0)
+                return r;
+
+        if (!strv_isempty(new_secret->password)) {
+                /* Update the password only if one is specified, otherwise let's just reuse the old password
+                 * data. This is useful as a way to propagate updated user records into the LUKS backends
+                 * properly. */
+
+                r = user_record_make_hashed_password(c, new_secret->password, /* extend = */ false);
+                if (r < 0)
+                        return r;
+
+                r = user_record_set_password_change_now(c, -1 /* remove */);
+                if (r < 0)
+                        return r;
+        }
+
+        r = user_record_update_last_changed(c, true);
+        if (r == -ECHRNG)
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_MISMATCH, "Record last change time of %s is newer than current time, cannot update.", h->user_name);
+        if (r < 0)
+                return r;
+
+        r = manager_sign_user_record(h->manager, c, &signed_c, error);
+        if (r < 0)
+                return r;
+
+        if (c->enforce_password_policy == false)
+                log_debug("Password quality check turned off for account, skipping.");
+        else {
+                r = quality_check_password(c, merged_secret, error);
+                if (r < 0)
+                        return r;
+        }
+
+        r = home_update_internal(h, "passwd", signed_c, merged_secret, error);
+        if (r < 0)
+                return r;
+
+        home_set_state(h, state == HOME_ACTIVE ? HOME_PASSWD_WHILE_ACTIVE : HOME_PASSWD);
+        return 0;
+}
+
+int home_unregister(Home *h, sd_bus_error *error) {
+        int r;
+
+        assert(h);
+
+        switch (home_get_state(h)) {
+        case HOME_UNFIXATED:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_UNFIXATED, "Home %s is not registered.", h->user_name);
+        case HOME_LOCKED:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+        case HOME_ABSENT:
+        case HOME_INACTIVE:
+                break;
+        case HOME_ACTIVE:
+        default:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
+        }
+
+        r = home_unlink_record(h);
+        if (r < 0)
+                return r;
+
+        /* And destroy the whole entry. The caller needs to be prepared for that. */
+        h = home_free(h);
+        return 1;
+}
+
+int home_lock(Home *h, sd_bus_error *error) {
+        int r;
+
+        assert(h);
+
+        switch (home_get_state(h)) {
+        case HOME_UNFIXATED:
+        case HOME_ABSENT:
+        case HOME_INACTIVE:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_ACTIVE, "Home %s is not active.", h->user_name);
+        case HOME_LOCKED:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is already locked.", h->user_name);
+        case HOME_ACTIVE:
+                break;
+        default:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
+        }
+
+        r = home_start_work(h, "lock", h->record, NULL);
+        if (r < 0)
+                return r;
+
+        home_set_state(h, HOME_LOCKING);
+        return 0;
+}
+
+static int home_unlock_internal(Home *h, UserRecord *secret, HomeState for_state, sd_bus_error *error) {
+        int r;
+
+        assert(h);
+        assert(IN_SET(for_state, HOME_UNLOCKING, HOME_UNLOCKING_FOR_ACQUIRE));
+
+        r = home_start_work(h, "unlock", h->record, secret);
+        if (r < 0)
+                return r;
+
+        home_set_state(h, for_state);
+        return 0;
+}
+
+int home_unlock(Home *h, UserRecord *secret, sd_bus_error *error) {
+        int r;
+        assert(h);
+
+        r = home_ratelimit(h, error);
+        if (r < 0)
+                return r;
+
+        switch (home_get_state(h)) {
+        case HOME_UNFIXATED:
+        case HOME_ABSENT:
+        case HOME_INACTIVE:
+        case HOME_ACTIVE:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_LOCKED, "Home %s is not locked.", h->user_name);
+        case HOME_LOCKED:
+                break;
+        default:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
+        }
+
+        return home_unlock_internal(h, secret, HOME_UNLOCKING, error);
+}
+
+HomeState home_get_state(Home *h) {
+        assert(h);
+
+        /* When the state field is initialized, it counts. */
+        if (h->state >= 0)
+                return h->state;
+
+        /* Otherwise, let's see if the home directory is mounted. If so, we assume for sure the home
+         * directory is active */
+        if (user_record_test_home_directory(h->record) == USER_TEST_MOUNTED)
+                return HOME_ACTIVE;
+
+        /* And if we see the image being gone, we report this as absent */
+        if (user_record_test_image_path(h->record) == USER_TEST_ABSENT)
+                return HOME_ABSENT;
+
+        /* And for all other cases we return "inactive". */
+        return HOME_INACTIVE;
+}
+
+void home_process_notify(Home *h, char **l) {
+        const char *e;
+        int error;
+        int r;
+
+        assert(h);
+
+        e = strv_env_get(l, "ERRNO");
+        if (!e) {
+                log_debug("Got notify message lacking ERRNO= field, ignoring.");
+                return;
+        }
+
+        r = safe_atoi(e, &error);
+        if (r < 0) {
+                log_debug_errno(r, "Failed to parse receieved error number, ignoring: %s", e);
+                return;
+        }
+        if (error <= 0) {
+                log_debug("Error number is out of range: %i", error);
+                return;
+        }
+
+        h->worker_error_code = error;
+}
+
+int home_killall(Home *h) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_free_ char *unit = NULL;
+        int r;
+
+        assert(h);
+
+        if (!uid_is_valid(h->uid))
+                return 0;
+
+        assert(h->uid > 0); /* We never should be UID 0 */
+
+        /* Let's kill everything matching the specified UID */
+        r = safe_fork("(sd-killer)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT|FORK_LOG, NULL);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                gid_t gid;
+
+                /* Child */
+
+                gid = user_record_gid(h->record);
+                if (setresgid(gid, gid, gid) < 0) {
+                        log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
+                        _exit(EXIT_FAILURE);
+                }
+
+                if (setgroups(0, NULL) < 0) {
+                        log_error_errno(errno, "Failed to reset auxiliary groups list: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                if (setresuid(h->uid, h->uid, h->uid) < 0) {
+                        log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", h->uid);
+                        _exit(EXIT_FAILURE);
+                }
+
+                if (kill(-1, SIGKILL) < 0) {
+                        log_error_errno(errno, "Failed to kill all processes of UID " UID_FMT ": %m", h->uid);
+                        _exit(EXIT_FAILURE);
+                }
+
+                _exit(EXIT_SUCCESS);
+        }
+
+        /* Let's also kill everything in the user's slice */
+        if (asprintf(&unit, "user-" UID_FMT ".slice", h->uid) < 0)
+                return log_oom();
+
+        r = sd_bus_call_method(
+                        h->manager->bus,
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        "KillUnit",
+                        &error,
+                        NULL,
+                        "ssi", unit, "all", SIGKILL);
+        if (r < 0)
+                log_full_errno(sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ? LOG_DEBUG : LOG_WARNING,
+                               r, "Failed to kill login processes of user, ignoring: %s", bus_error_message(&error, r));
+
+        return 1;
+}
+
+static int home_get_disk_status_luks(
+                Home *h,
+                HomeState state,
+                uint64_t *ret_disk_size,
+                uint64_t *ret_disk_usage,
+                uint64_t *ret_disk_free,
+                uint64_t *ret_disk_ceiling,
+                uint64_t *ret_disk_floor) {
+
+        uint64_t disk_size = UINT64_MAX, disk_usage = UINT64_MAX, disk_free = UINT64_MAX,
+                disk_ceiling = UINT64_MAX, disk_floor = UINT64_MAX,
+                stat_used = UINT64_MAX, fs_size = UINT64_MAX, header_size = 0;
+
+        struct statfs sfs;
+        const char *hd;
+        int r;
+
+        assert(h);
+        assert(ret_disk_size);
+        assert(ret_disk_usage);
+        assert(ret_disk_free);
+        assert(ret_disk_ceiling);
+
+        if (state != HOME_ABSENT) {
+                const char *ip;
+
+                ip = user_record_image_path(h->record);
+                if (ip) {
+                        struct stat st;
+
+                        if (stat(ip, &st) < 0)
+                                log_debug_errno(errno, "Failed to stat() %s, ignoring: %m", ip);
+                        else if (S_ISREG(st.st_mode)) {
+                                _cleanup_free_ char *parent = NULL;
+
+                                disk_size = st.st_size;
+                                stat_used = st.st_blocks * 512;
+
+                                parent = dirname_malloc(ip);
+                                if (!parent)
+                                        return log_oom();
+
+                                if (statfs(parent, &sfs) < 0)
+                                        log_debug_errno(errno, "Failed to statfs() %s, ignoring: %m", parent);
+                                else
+                                        disk_ceiling = stat_used + sfs.f_bsize * sfs.f_bavail;
+
+                        } else if (S_ISBLK(st.st_mode)) {
+                                _cleanup_free_ char *szbuf = NULL;
+                                char p[SYS_BLOCK_PATH_MAX("/size")];
+
+                                /* Let's read the size off sysfs, so that we don't have to open the device */
+                                xsprintf_sys_block_path(p, "/size", st.st_rdev);
+                                r = read_one_line_file(p, &szbuf);
+                                if (r < 0)
+                                        log_debug_errno(r, "Failed to read %s, ignoring: %m", p);
+                                else {
+                                        uint64_t sz;
+
+                                        r = safe_atou64(szbuf, &sz);
+                                        if (r < 0)
+                                                log_debug_errno(r, "Failed to parse %s, ignoring: %s", p, szbuf);
+                                        else
+                                                disk_size = sz * 512;
+                                }
+                        } else
+                                log_debug("Image path is not a block device or regular file, not able to acquire size.");
+                }
+        }
+
+        if (!HOME_STATE_IS_ACTIVE(state))
+                goto finish;
+
+        hd = user_record_home_directory(h->record);
+        if (!hd)
+                goto finish;
+
+        if (statfs(hd, &sfs) < 0) {
+                log_debug_errno(errno, "Failed  to statfs() %s, ignoring: %m", hd);
+                goto finish;
+        }
+
+        disk_free = sfs.f_bsize * sfs.f_bavail;
+        fs_size = sfs.f_bsize * sfs.f_blocks;
+        if (disk_size != UINT64_MAX && disk_size > fs_size)
+                header_size = disk_size - fs_size;
+
+        /* We take a perspective from the user here (as opposed to from the host): the used disk space is the
+         * difference from the limit and what's free. This makes a difference if sparse mode is not used: in
+         * that case the image is pre-allocated and thus appears all used from the host PoV but is not used
+         * up at all yet from the user's PoV.
+         *
+         * That said, we use use the stat() reported loopback file size as upper boundary: our footprint can
+         * never be larger than what we take up on the lowest layers. */
+
+        if (disk_size != UINT64_MAX && disk_size > disk_free) {
+                disk_usage = disk_size - disk_free;
+
+                if (stat_used != UINT64_MAX && disk_usage > stat_used)
+                        disk_usage = stat_used;
+        } else
+                disk_usage = stat_used;
+
+        /* If we have the magic, determine floor preferably by magic */
+        disk_floor = minimal_size_by_fs_magic(sfs.f_type) + header_size;
+
+finish:
+        /* If we don't know the magic, go by file system name */
+        if (disk_floor == UINT64_MAX)
+                disk_floor = minimal_size_by_fs_name(user_record_file_system_type(h->record));
+
+        *ret_disk_size = disk_size;
+        *ret_disk_usage = disk_usage;
+        *ret_disk_free = disk_free;
+        *ret_disk_ceiling = disk_ceiling;
+        *ret_disk_floor = disk_floor;
+
+        return 0;
+}
+
+static int home_get_disk_status_directory(
+                Home *h,
+                HomeState state,
+                uint64_t *ret_disk_size,
+                uint64_t *ret_disk_usage,
+                uint64_t *ret_disk_free,
+                uint64_t *ret_disk_ceiling,
+                uint64_t *ret_disk_floor) {
+
+        uint64_t disk_size = UINT64_MAX, disk_usage = UINT64_MAX, disk_free = UINT64_MAX,
+                disk_ceiling = UINT64_MAX, disk_floor = UINT64_MAX;
+        struct statfs sfs;
+        struct dqblk req;
+        const char *path = NULL;
+        int r;
+
+        assert(ret_disk_size);
+        assert(ret_disk_usage);
+        assert(ret_disk_free);
+        assert(ret_disk_ceiling);
+        assert(ret_disk_floor);
+
+        if (HOME_STATE_IS_ACTIVE(state))
+                path = user_record_home_directory(h->record);
+
+        if (!path) {
+                if (state == HOME_ABSENT)
+                        goto finish;
+
+                path = user_record_image_path(h->record);
+        }
+
+        if (!path)
+                goto finish;
+
+        if (statfs(path, &sfs) < 0)
+                log_debug_errno(errno, "Failed to statfs() %s, ignoring: %m", path);
+        else {
+                disk_free = sfs.f_bsize * sfs.f_bavail;
+                disk_size = sfs.f_bsize * sfs.f_blocks;
+
+                /* We don't initialize disk_usage from statfs() data here, since the device is likely not used
+                 * by us alone, and disk_usage should only reflect our own use. */
+        }
+
+        if (IN_SET(h->record->storage, USER_CLASSIC, USER_DIRECTORY, USER_SUBVOLUME)) {
+
+                r = btrfs_is_subvol(path);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to determine whether %s is a btrfs subvolume: %m", path);
+                else if (r > 0) {
+                        BtrfsQuotaInfo qi;
+
+                        r = btrfs_subvol_get_subtree_quota(path, 0, &qi);
+                        if (r < 0)
+                                log_debug_errno(r, "Failed to query btrfs subtree quota, ignoring: %m");
+                        else {
+                                disk_usage = qi.referenced;
+
+                                if (disk_free != UINT64_MAX) {
+                                        disk_ceiling = qi.referenced + disk_free;
+
+                                        if (disk_size != UINT64_MAX && disk_ceiling > disk_size)
+                                                disk_ceiling = disk_size;
+                                }
+
+                                if (qi.referenced_max != UINT64_MAX) {
+                                        if (disk_size != UINT64_MAX)
+                                                disk_size = MIN(qi.referenced_max, disk_size);
+                                        else
+                                                disk_size = qi.referenced_max;
+                                }
+
+                                if (disk_size != UINT64_MAX) {
+                                        if (disk_size > disk_usage)
+                                                disk_free = disk_size - disk_usage;
+                                        else
+                                                disk_free = 0;
+                                }
+                        }
+
+                        goto finish;
+                }
+        }
+
+        if (IN_SET(h->record->storage, USER_CLASSIC, USER_DIRECTORY, USER_FSCRYPT)) {
+                r = quotactl_path(QCMD_FIXED(Q_GETQUOTA, USRQUOTA), path, h->uid, &req);
+                if (r < 0) {
+                        if (ERRNO_IS_NOT_SUPPORTED(r)) {
+                                log_debug_errno(r, "No UID quota support on %s.", path);
+                                goto finish;
+                        }
+
+                        if (r != -ESRCH) {
+                                log_debug_errno(r, "Failed to query disk quota for UID " UID_FMT ": %m", h->uid);
+                                goto finish;
+                        }
+
+                        disk_usage = 0; /* No record of this user? then nothing was used */
+                } else {
+                        if (FLAGS_SET(req.dqb_valid, QIF_SPACE) && disk_free != UINT64_MAX) {
+                                disk_ceiling = req.dqb_curspace + disk_free;
+
+                                if (disk_size != UINT64_MAX && disk_ceiling > disk_size)
+                                        disk_ceiling = disk_size;
+                        }
+
+                        if (FLAGS_SET(req.dqb_valid, QIF_BLIMITS)) {
+                                uint64_t q;
+
+                                /* Take the minimum of the quota and the available disk space here */
+                                q = req.dqb_bhardlimit * QIF_DQBLKSIZE;
+                                if (disk_size != UINT64_MAX)
+                                        disk_size = MIN(disk_size, q);
+                                else
+                                        disk_size = q;
+                        }
+                        if (FLAGS_SET(req.dqb_valid, QIF_SPACE)) {
+                                disk_usage = req.dqb_curspace;
+
+                                if (disk_size != UINT64_MAX) {
+                                        if (disk_size > disk_usage)
+                                                disk_free = disk_size - disk_usage;
+                                        else
+                                                disk_free = 0;
+                                }
+                        }
+                }
+        }
+
+finish:
+        *ret_disk_size = disk_size;
+        *ret_disk_usage = disk_usage;
+        *ret_disk_free = disk_free;
+        *ret_disk_ceiling = disk_ceiling;
+        *ret_disk_floor = disk_floor;
+
+        return 0;
+}
+
+int home_augment_status(
+                Home *h,
+                UserRecordLoadFlags flags,
+                UserRecord **ret) {
+
+        uint64_t disk_size = UINT64_MAX, disk_usage = UINT64_MAX, disk_free = UINT64_MAX, disk_ceiling = UINT64_MAX, disk_floor = UINT64_MAX;
+        _cleanup_(json_variant_unrefp) JsonVariant *j = NULL, *v = NULL, *m = NULL, *status = NULL;
+        _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+        char ids[SD_ID128_STRING_MAX];
+        HomeState state;
+        sd_id128_t id;
+        int r;
+
+        assert(h);
+        assert(ret);
+
+        /* We are supposed to add this, this can't be on hence. */
+        assert(!FLAGS_SET(flags, USER_RECORD_STRIP_STATUS));
+
+        r = sd_id128_get_machine(&id);
+        if (r < 0)
+                return r;
+
+        state = home_get_state(h);
+
+        switch (h->record->storage) {
+
+        case USER_LUKS:
+                r = home_get_disk_status_luks(h, state, &disk_size, &disk_usage, &disk_free, &disk_ceiling, &disk_floor);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case USER_CLASSIC:
+        case USER_DIRECTORY:
+        case USER_SUBVOLUME:
+        case USER_FSCRYPT:
+        case USER_CIFS:
+                r = home_get_disk_status_directory(h, state, &disk_size, &disk_usage, &disk_free, &disk_ceiling, &disk_floor);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        default:
+                ; /* unset */
+        }
+
+        if (disk_floor == UINT64_MAX || (disk_usage != UINT64_MAX && disk_floor < disk_usage))
+                disk_floor = disk_usage;
+        if (disk_floor == UINT64_MAX || disk_floor < USER_DISK_SIZE_MIN)
+                disk_floor = USER_DISK_SIZE_MIN;
+        if (disk_ceiling == UINT64_MAX || disk_ceiling > USER_DISK_SIZE_MAX)
+                disk_ceiling = USER_DISK_SIZE_MAX;
+
+        r = json_build(&status,
+                       JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR("state", JSON_BUILD_STRING(home_state_to_string(state))),
+                                       JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.Home")),
+                                       JSON_BUILD_PAIR_CONDITION(disk_size != UINT64_MAX, "diskSize", JSON_BUILD_UNSIGNED(disk_size)),
+                                       JSON_BUILD_PAIR_CONDITION(disk_usage != UINT64_MAX, "diskUsage", JSON_BUILD_UNSIGNED(disk_usage)),
+                                       JSON_BUILD_PAIR_CONDITION(disk_free != UINT64_MAX, "diskFree", JSON_BUILD_UNSIGNED(disk_free)),
+                                       JSON_BUILD_PAIR_CONDITION(disk_ceiling != UINT64_MAX, "diskCeiling", JSON_BUILD_UNSIGNED(disk_ceiling)),
+                                       JSON_BUILD_PAIR_CONDITION(disk_floor != UINT64_MAX, "diskFloor", JSON_BUILD_UNSIGNED(disk_floor)),
+                                       JSON_BUILD_PAIR_CONDITION(h->signed_locally >= 0, "signedLocally", JSON_BUILD_BOOLEAN(h->signed_locally))
+                       ));
+        if (r < 0)
+                return r;
+
+        j = json_variant_ref(h->record->json);
+        v = json_variant_ref(json_variant_by_key(j, "status"));
+        m = json_variant_ref(json_variant_by_key(v, sd_id128_to_string(id, ids)));
+
+        r = json_variant_filter(&m, STRV_MAKE("diskSize", "diskUsage", "diskFree", "diskCeiling", "diskFloor", "signedLocally"));
+        if (r < 0)
+                return r;
+
+        r = json_variant_merge(&m, status);
+        if (r < 0)
+                return r;
+
+        r = json_variant_set_field(&v, ids, m);
+        if (r < 0)
+                return r;
+
+        r = json_variant_set_field(&j, "status", v);
+        if (r < 0)
+                return r;
+
+        ur = user_record_new();
+        if (!ur)
+                return -ENOMEM;
+
+        r = user_record_load(ur, j, flags);
+        if (r < 0)
+                return r;
+
+        ur->incomplete =
+                FLAGS_SET(h->record->mask, USER_RECORD_PRIVILEGED) &&
+                !FLAGS_SET(ur->mask, USER_RECORD_PRIVILEGED);
+
+        *ret = TAKE_PTR(ur);
+        return 0;
+}
+
+static int on_home_ref_eof(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        _cleanup_(operation_unrefp) Operation *o = NULL;
+        Home *h = userdata;
+
+        assert(s);
+        assert(h);
+
+        if (h->ref_event_source_please_suspend == s)
+                h->ref_event_source_please_suspend = sd_event_source_disable_unref(h->ref_event_source_please_suspend);
+
+        if (h->ref_event_source_dont_suspend == s)
+                h->ref_event_source_dont_suspend = sd_event_source_disable_unref(h->ref_event_source_dont_suspend);
+
+        if (h->ref_event_source_dont_suspend || h->ref_event_source_please_suspend)
+                return 0;
+
+        log_info("Got notification that all sessions of user %s ended, deactivating automatically.", h->user_name);
+
+        o = operation_new(OPERATION_PIPE_EOF, NULL);
+        if (!o) {
+                log_oom();
+                return 0;
+        }
+
+        home_schedule_operation(h, o, NULL);
+        return 0;
+}
+
+int home_create_fifo(Home *h, bool please_suspend) {
+        _cleanup_close_ int ret_fd = -1;
+        sd_event_source **ss;
+        const char *fn, *suffix;
+        int r;
+
+        assert(h);
+
+        if (please_suspend) {
+                suffix = ".please-suspend";
+                ss = &h->ref_event_source_please_suspend;
+        } else {
+                suffix = ".dont-suspend";
+                ss = &h->ref_event_source_dont_suspend;
+        }
+
+        fn = strjoina("/run/systemd/home/", h->user_name, suffix);
+
+        if (!*ss) {
+                _cleanup_close_ int ref_fd = -1;
+
+                (void) mkdir("/run/systemd/home/", 0755);
+                if (mkfifo(fn, 0600) < 0 && errno != EEXIST)
+                        return log_error_errno(errno, "Failed to create FIFO %s: %m", fn);
+
+                ref_fd = open(fn, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+                if (ref_fd < 0)
+                        return log_error_errno(errno, "Failed to open FIFO %s for reading: %m", fn);
+
+                r = sd_event_add_io(h->manager->event, ss, ref_fd, 0, on_home_ref_eof, h);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to allocate reference FIFO event source: %m");
+
+                (void) sd_event_source_set_description(*ss, "acquire-ref");
+
+                r = sd_event_source_set_priority(*ss, SD_EVENT_PRIORITY_IDLE-1);
+                if (r < 0)
+                        return r;
+
+                r = sd_event_source_set_io_fd_own(*ss, true);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to pass ownership of FIFO event fd to event source: %m");
+
+                TAKE_FD(ref_fd);
+        }
+
+        ret_fd = open(fn, O_WRONLY|O_CLOEXEC|O_NONBLOCK);
+        if (ret_fd < 0)
+                return log_error_errno(errno, "Failed to open FIFO %s for writing: %m", fn);
+
+        return TAKE_FD(ret_fd);
+}
+
+static int home_dispatch_acquire(Home *h, Operation *o) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        int (*call)(Home *h, UserRecord *secret, HomeState for_state, sd_bus_error *error) = NULL;
+        HomeState for_state;
+        int r;
+
+        assert(h);
+        assert(o);
+        assert(o->type == OPERATION_ACQUIRE);
+
+        switch (home_get_state(h)) {
+
+        case HOME_UNFIXATED:
+                for_state = HOME_FIXATING_FOR_ACQUIRE;
+                call = home_fixate_internal;
+                break;
+
+        case HOME_ABSENT:
+                r = sd_bus_error_setf(&error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
+                break;
+
+        case HOME_INACTIVE:
+                for_state = HOME_ACTIVATING_FOR_ACQUIRE;
+                call = home_activate_internal;
+                break;
+
+        case HOME_ACTIVE:
+                for_state = HOME_AUTHENTICATING_FOR_ACQUIRE;
+                call = home_authenticate_internal;
+                break;
+
+        case HOME_LOCKED:
+                for_state = HOME_UNLOCKING_FOR_ACQUIRE;
+                call = home_unlock_internal;
+                break;
+
+        default:
+                /* All other cases means we are currently executing an operation, which means the job remains
+                 * pending. */
+                return 0;
+        }
+
+        assert(!h->current_operation);
+
+        if (call) {
+                r = home_ratelimit(h, &error);
+                if (r >= 0)
+                        r = call(h, o->secret, for_state, &error);
+        }
+
+        if (r != 0) /* failure or completed */
+                operation_result(o, r, &error);
+        else /* ongoing */
+                h->current_operation = operation_ref(o);
+
+        return 1;
+}
+
+static int home_dispatch_release(Home *h, Operation *o) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        int r;
+
+        assert(h);
+        assert(o);
+        assert(o->type == OPERATION_RELEASE);
+
+        if (h->ref_event_source_dont_suspend || h->ref_event_source_please_suspend)
+                /* If there's now a reference again, then let's abort the release attempt */
+                r = sd_bus_error_setf(&error, BUS_ERROR_HOME_BUSY, "Home %s is currently referenced.", h->user_name);
+        else {
+                switch (home_get_state(h)) {
+
+                case HOME_UNFIXATED:
+                case HOME_ABSENT:
+                case HOME_INACTIVE:
+                        r = 0; /* done */
+                        break;
+
+                case HOME_LOCKED:
+                        r = sd_bus_error_setf(&error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+                        break;
+
+                case HOME_ACTIVE:
+                        r = home_deactivate_internal(h, false, &error);
+                        break;
+
+                default:
+                        /* All other cases means we are currently executing an operation, which means the job remains
+                         * pending. */
+                        return 0;
+                }
+        }
+
+        assert(!h->current_operation);
+
+        if (r <= 0) /* failure or completed */
+                operation_result(o, r, &error);
+        else /* ongoing */
+                h->current_operation = operation_ref(o);
+
+        return 1;
+}
+
+static int home_dispatch_lock_all(Home *h, Operation *o) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        int r;
+
+        assert(h);
+        assert(o);
+        assert(o->type == OPERATION_LOCK_ALL);
+
+        switch (home_get_state(h)) {
+
+        case HOME_UNFIXATED:
+        case HOME_ABSENT:
+        case HOME_INACTIVE:
+                log_info("Home %s is not active, no locking necessary.", h->user_name);
+                r = 0; /* done */
+                break;
+
+        case HOME_LOCKED:
+                log_info("Home %s is already locked.", h->user_name);
+                r = 0; /* done */
+                break;
+
+        case HOME_ACTIVE:
+                log_info("Locking home %s.", h->user_name);
+                r = home_lock(h, &error);
+                break;
+
+        default:
+                /* All other cases means we are currently executing an operation, which means the job remains
+                 * pending. */
+                return 0;
+        }
+
+        assert(!h->current_operation);
+
+        if (r != 0) /* failure or completed */
+                operation_result(o, r, &error);
+        else /* ongoing */
+                h->current_operation = operation_ref(o);
+
+        return 1;
+}
+
+static int home_dispatch_pipe_eof(Home *h, Operation *o) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        int r;
+
+        assert(h);
+        assert(o);
+        assert(o->type == OPERATION_PIPE_EOF);
+
+        if (h->ref_event_source_please_suspend || h->ref_event_source_dont_suspend)
+                return 1; /* Hmm, there's a reference again, let's cancel this */
+
+        switch (home_get_state(h)) {
+
+        case HOME_UNFIXATED:
+        case HOME_ABSENT:
+        case HOME_INACTIVE:
+                log_info("Home %s already deactivated, no automatic deactivation needed.", h->user_name);
+                break;
+
+        case HOME_DEACTIVATING:
+                log_info("Home %s is already being deactivated, automatic deactivated unnecessary.", h->user_name);
+                break;
+
+        case HOME_ACTIVE:
+                r = home_deactivate_internal(h, false, &error);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to deactivate %s, ignoring: %s", h->user_name, bus_error_message(&error, r));
+                break;
+
+        case HOME_LOCKED:
+        default:
+                /* If the device is locked or any operation is being executed, let's leave this pending */
+                return 0;
+        }
+
+        /* Note that we don't call operation_fail() or operation_success() here, because this kind of
+         * operation has no message associated with it, and thus there's no need to propagate success. */
+
+        assert(!o->message);
+        return 1;
+}
+
+static int home_dispatch_deactivate_force(Home *h, Operation *o) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        int r;
+
+        assert(h);
+        assert(o);
+        assert(o->type == OPERATION_DEACTIVATE_FORCE);
+
+        switch (home_get_state(h)) {
+
+        case HOME_UNFIXATED:
+        case HOME_ABSENT:
+        case HOME_INACTIVE:
+                log_debug("Home %s already deactivated, no forced deactivation due to unplug needed.", h->user_name);
+                break;
+
+        case HOME_DEACTIVATING:
+                log_debug("Home %s is already being deactivated, forced deactivation due to unplug unnecessary.", h->user_name);
+                break;
+
+        case HOME_ACTIVE:
+        case HOME_LOCKED:
+                r = home_deactivate_internal(h, true, &error);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to forcibly deactivate %s, ignoring: %s", h->user_name, bus_error_message(&error, r));
+                break;
+
+        default:
+                /* If any operation is being executed, let's leave this pending */
+                return 0;
+        }
+
+        /* Note that we don't call operation_fail() or operation_success() here, because this kind of
+         * operation has no message associated with it, and thus there's no need to propagate success. */
+
+        assert(!o->message);
+        return 1;
+}
+
+static int on_pending(sd_event_source *s, void *userdata) {
+        Home *h = userdata;
+        Operation *o;
+        int r;
+
+        assert(s);
+        assert(h);
+
+        o = ordered_set_first(h->pending_operations);
+        if (o) {
+                static int (* const operation_table[_OPERATION_MAX])(Home *h, Operation *o) = {
+                        [OPERATION_ACQUIRE]          = home_dispatch_acquire,
+                        [OPERATION_RELEASE]          = home_dispatch_release,
+                        [OPERATION_LOCK_ALL]         = home_dispatch_lock_all,
+                        [OPERATION_PIPE_EOF]         = home_dispatch_pipe_eof,
+                        [OPERATION_DEACTIVATE_FORCE] = home_dispatch_deactivate_force,
+                };
+
+                assert(operation_table[o->type]);
+                r = operation_table[o->type](h, o);
+                if (r != 0) {
+                        /* The operation completed, let's remove it from the pending list, and exit while
+                         * leaving the event source enabled as it is. */
+                        assert_se(ordered_set_remove(h->pending_operations, o) == o);
+                        operation_unref(o);
+                        return 0;
+                }
+        }
+
+        /* Nothing to do anymore, let's turn off this event source */
+        r = sd_event_source_set_enabled(s, SD_EVENT_OFF);
+        if (r < 0)
+                return log_error_errno(r, "Failed to disable event source: %m");
+
+        return 0;
+}
+
+int home_schedule_operation(Home *h, Operation *o, sd_bus_error *error) {
+        int r;
+
+        assert(h);
+
+        if (o) {
+                if (ordered_set_size(h->pending_operations) >= PENDING_OPERATIONS_MAX)
+                        return sd_bus_error_setf(error, BUS_ERROR_TOO_MANY_OPERATIONS, "Too many client operations requested");
+
+                r = ordered_set_ensure_allocated(&h->pending_operations, &operation_hash_ops);
+                if (r < 0)
+                        return r;
+
+                r = ordered_set_put(h->pending_operations, o);
+                if (r < 0)
+                        return r;
+
+                operation_ref(o);
+        }
+
+        if (!h->pending_event_source) {
+                r = sd_event_add_defer(h->manager->event, &h->pending_event_source, on_pending, h);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to allocate pending defer event source: %m");
+
+                (void) sd_event_source_set_description(h->pending_event_source, "pending");
+
+                r = sd_event_source_set_priority(h->pending_event_source, SD_EVENT_PRIORITY_IDLE);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_event_source_set_enabled(h->pending_event_source, SD_EVENT_ON);
+        if (r < 0)
+                return log_error_errno(r, "Failed to trigger pending event source: %m");
+
+        return 0;
+}
+
+static int home_get_image_path_seat(Home *h, char **ret) {
+        _cleanup_(sd_device_unrefp) sd_device *d = NULL;
+        _cleanup_free_ char *c = NULL;
+        const char *ip, *seat;
+        struct stat st;
+        int r;
+
+        assert(h);
+
+        if (user_record_storage(h->record) != USER_LUKS)
+                return -ENXIO;
+
+        ip = user_record_image_path(h->record);
+        if (!ip)
+                return -ENXIO;
+
+        if (!path_startswith(ip, "/dev/"))
+                return -ENXIO;
+
+        if (stat(ip, &st) < 0)
+                return -errno;
+
+        if (!S_ISBLK(st.st_mode))
+                return -ENOTBLK;
+
+        r = sd_device_new_from_devnum(&d, 'b', st.st_rdev);
+        if (r < 0)
+                return r;
+
+        r = sd_device_get_property_value(d, "ID_SEAT", &seat);
+        if (r == -ENOENT) /* no property means seat0 */
+                seat = "seat0";
+        else if (r < 0)
+                return r;
+
+        c = strdup(seat);
+        if (!c)
+                return -ENOMEM;
+
+        *ret = TAKE_PTR(c);
+        return 0;
+}
+
+int home_auto_login(Home *h, char ***ret_seats) {
+        _cleanup_free_ char *seat = NULL, *seat2 = NULL;
+
+        assert(h);
+        assert(ret_seats);
+
+        (void) home_get_image_path_seat(h, &seat);
+
+        if (h->record->auto_login > 0 && !streq_ptr(seat, "seat0")) {
+                /* For now, when the auto-login boolean is set for a user, let's make it mean
+                 * "seat0". Eventually we can extend the concept and allow configuration of any kind of seat,
+                 * but let's keep simple initially, most likely the feature is interesting on single-user
+                 * systems anyway, only.
+                 *
+                 * We filter out users marked for auto-login in we know for sure their home directory is
+                 * absent. */
+
+                if (user_record_test_image_path(h->record) != USER_TEST_ABSENT) {
+                        seat2 = strdup("seat0");
+                        if (!seat2)
+                                return -ENOMEM;
+                }
+        }
+
+        if (seat || seat2) {
+                _cleanup_strv_free_ char **list = NULL;
+                size_t i = 0;
+
+                list = new(char*, 3);
+                if (!list)
+                        return -ENOMEM;
+
+                if (seat)
+                        list[i++] = TAKE_PTR(seat);
+                if (seat2)
+                        list[i++] = TAKE_PTR(seat2);
+
+                list[i] = NULL;
+                *ret_seats = TAKE_PTR(list);
+                return 1;
+        }
+
+        *ret_seats = NULL;
+        return 0;
+}
+
+int home_set_current_message(Home *h, sd_bus_message *m) {
+        assert(h);
+
+        if (!m)
+                return 0;
+
+        if (h->current_operation)
+                return -EBUSY;
+
+        h->current_operation = operation_new(OPERATION_IMMEDIATE, m);
+        if (!h->current_operation)
+                return -ENOMEM;
+
+        return 1;
+}
+
+static const char* const home_state_table[_HOME_STATE_MAX] = {
+        [HOME_UNFIXATED]                   = "unfixated",
+        [HOME_ABSENT]                      = "absent",
+        [HOME_INACTIVE]                    = "inactive",
+        [HOME_FIXATING]                    = "fixating",
+        [HOME_FIXATING_FOR_ACTIVATION]     = "fixating-for-activation",
+        [HOME_FIXATING_FOR_ACQUIRE]        = "fixating-for-acquire",
+        [HOME_ACTIVATING]                  = "activating",
+        [HOME_ACTIVATING_FOR_ACQUIRE]      = "activating-for-acquire",
+        [HOME_DEACTIVATING]                = "deactivating",
+        [HOME_ACTIVE]                      = "active",
+        [HOME_LOCKING]                     = "locking",
+        [HOME_LOCKED]                      = "locked",
+        [HOME_UNLOCKING]                   = "unlocking",
+        [HOME_UNLOCKING_FOR_ACQUIRE]       = "unlocking-for-acquire",
+        [HOME_CREATING]                    = "creating",
+        [HOME_REMOVING]                    = "removing",
+        [HOME_UPDATING]                    = "updating",
+        [HOME_UPDATING_WHILE_ACTIVE]       = "updating-while-active",
+        [HOME_RESIZING]                    = "resizing",
+        [HOME_RESIZING_WHILE_ACTIVE]       = "resizing-while-active",
+        [HOME_PASSWD]                      = "passwd",
+        [HOME_PASSWD_WHILE_ACTIVE]         = "passwd-while-active",
+        [HOME_AUTHENTICATING]              = "authenticating",
+        [HOME_AUTHENTICATING_WHILE_ACTIVE] = "authenticating-while-active",
+        [HOME_AUTHENTICATING_FOR_ACQUIRE]  = "authenticating-for-acquire",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(home_state, HomeState);
diff --git a/src/home/homed-home.h b/src/home/homed-home.h
new file mode 100644 (file)
index 0000000..ec7966f
--- /dev/null
@@ -0,0 +1,168 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+typedef struct Home Home;
+
+#include "homed-manager.h"
+#include "homed-operation.h"
+#include "list.h"
+#include "ordered-set.h"
+#include "user-record.h"
+
+typedef enum HomeState {
+        HOME_UNFIXATED,               /* home exists, but local record does not */
+        HOME_ABSENT,                  /* local record exists, but home does not */
+        HOME_INACTIVE,                /* record and home exist, but is not logged in */
+        HOME_FIXATING,                /* generating local record from home */
+        HOME_FIXATING_FOR_ACTIVATION, /* fixating in order to activate soon */
+        HOME_FIXATING_FOR_ACQUIRE,    /* fixating because Acquire() was called */
+        HOME_ACTIVATING,
+        HOME_ACTIVATING_FOR_ACQUIRE,  /* activating because Acquire() was called */
+        HOME_DEACTIVATING,
+        HOME_ACTIVE,                  /* logged in right now */
+        HOME_LOCKING,
+        HOME_LOCKED,
+        HOME_UNLOCKING,
+        HOME_UNLOCKING_FOR_ACQUIRE,   /* unlocking because Acquire() was called */
+        HOME_CREATING,
+        HOME_REMOVING,
+        HOME_UPDATING,
+        HOME_UPDATING_WHILE_ACTIVE,
+        HOME_RESIZING,
+        HOME_RESIZING_WHILE_ACTIVE,
+        HOME_PASSWD,
+        HOME_PASSWD_WHILE_ACTIVE,
+        HOME_AUTHENTICATING,
+        HOME_AUTHENTICATING_WHILE_ACTIVE,
+        HOME_AUTHENTICATING_FOR_ACQUIRE,  /* authenticating because Acquire() was called */
+        _HOME_STATE_MAX,
+        _HOME_STATE_INVALID = -1
+} HomeState;
+
+static inline bool HOME_STATE_IS_ACTIVE(HomeState state) {
+        return IN_SET(state,
+                      HOME_ACTIVE,
+                      HOME_UPDATING_WHILE_ACTIVE,
+                      HOME_RESIZING_WHILE_ACTIVE,
+                      HOME_PASSWD_WHILE_ACTIVE,
+                      HOME_AUTHENTICATING_WHILE_ACTIVE,
+                      HOME_AUTHENTICATING_FOR_ACQUIRE);
+}
+
+static inline bool HOME_STATE_IS_EXECUTING_OPERATION(HomeState state) {
+        return IN_SET(state,
+                      HOME_FIXATING,
+                      HOME_FIXATING_FOR_ACTIVATION,
+                      HOME_FIXATING_FOR_ACQUIRE,
+                      HOME_ACTIVATING,
+                      HOME_ACTIVATING_FOR_ACQUIRE,
+                      HOME_DEACTIVATING,
+                      HOME_LOCKING,
+                      HOME_UNLOCKING,
+                      HOME_UNLOCKING_FOR_ACQUIRE,
+                      HOME_CREATING,
+                      HOME_REMOVING,
+                      HOME_UPDATING,
+                      HOME_UPDATING_WHILE_ACTIVE,
+                      HOME_RESIZING,
+                      HOME_RESIZING_WHILE_ACTIVE,
+                      HOME_PASSWD,
+                      HOME_PASSWD_WHILE_ACTIVE,
+                      HOME_AUTHENTICATING,
+                      HOME_AUTHENTICATING_WHILE_ACTIVE,
+                      HOME_AUTHENTICATING_FOR_ACQUIRE);
+}
+
+struct Home {
+        Manager *manager;
+        char *user_name;
+        uid_t uid;
+
+        char *sysfs; /* When found via plugged in device, the sysfs path to it */
+
+        /* Note that the 'state' field is only set to a state while we are doing something (i.e. activating,
+         * deactivating, creating, removing, and such), or when the home is an "unfixated" one. When we are
+         * done with an operation we invalidate the state. This is hint for home_get_state() to check the
+         * state on request as needed from the mount table and similar.*/
+        HomeState state;
+        int signed_locally; /* signed only by us */
+
+        UserRecord *record;
+
+        pid_t worker_pid;
+        int worker_stdout_fd;
+        sd_event_source *worker_event_source;
+        int worker_error_code;
+
+        /* The message we are currently processing, and thus need to reply to on completion */
+        Operation *current_operation;
+
+        /* Stores the raw, plaintext passwords, but only for short periods of time */
+        UserRecord *secret;
+
+        /* When we create a home area and that fails, we should possibly unregister the record altogether
+         * again, which is remembered in this boolean. */
+        bool unregister_on_failure;
+
+        /* The reading side of a FIFO stored in /run/systemd/home/, the writing side being used for reference
+         * counting. The references dropped to zero as soon as we see EOF. This concept exists twice: once
+         * for clients that are fine if we suspend the home directory on system suspend, and once for cliets
+         * that are not ok with that. This allows us to determine for each home whether there are any clients
+         * that support unsuspend. */
+        sd_event_source *ref_event_source_please_suspend;
+        sd_event_source *ref_event_source_dont_suspend;
+
+        /* Any pending operations we still need to execute. These are for operations we want to queue if we
+         * can't execute them right-away. */
+        OrderedSet *pending_operations;
+
+        /* A defer event source that processes pending acquire/release/eof events. We have a common
+         * dispatcher that processes all three kinds of events. */
+        sd_event_source *pending_event_source;
+
+        /* Did we send out a D-Bus notification about this entry? */
+        bool announced;
+
+        /* Used to coalesce bus PropertiesChanged events */
+        sd_event_source *deferred_change_event_source;
+};
+
+int home_new(Manager *m, UserRecord *hr, const char *sysfs, Home **ret);
+Home *home_free(Home *h);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Home*, home_free);
+
+int home_set_record(Home *h, UserRecord *hr);
+int home_save_record(Home *h);
+int home_unlink_record(Home *h);
+
+int home_fixate(Home *h, UserRecord *secret, sd_bus_error *error);
+int home_activate(Home *h, UserRecord *secret, sd_bus_error *error);
+int home_authenticate(Home *h, UserRecord *secret, sd_bus_error *error);
+int home_deactivate(Home *h, bool force, sd_bus_error *error);
+int home_create(Home *h, UserRecord *secret, sd_bus_error *error);
+int home_remove(Home *h, sd_bus_error *error);
+int home_update(Home *h, UserRecord *new_record, sd_bus_error *error);
+int home_resize(Home *h, uint64_t disk_size, UserRecord *secret, sd_bus_error *error);
+int home_passwd(Home *h, UserRecord *new_secret, UserRecord *old_secret, sd_bus_error *error);
+int home_unregister(Home *h, sd_bus_error *error);
+int home_lock(Home *h, sd_bus_error *error);
+int home_unlock(Home *h, UserRecord *secret, sd_bus_error *error);
+
+HomeState home_get_state(Home *h);
+
+void home_process_notify(Home *h, char **l);
+
+int home_killall(Home *h);
+
+int home_augment_status(Home *h, UserRecordLoadFlags flags, UserRecord **ret);
+
+int home_create_fifo(Home *h, bool please_suspend);
+int home_schedule_operation(Home *h, Operation *o, sd_bus_error *error);
+
+int home_auto_login(Home *h, char ***ret_seats);
+
+int home_set_current_message(Home *h, sd_bus_message *m);
+
+const char *home_state_to_string(HomeState state);
+HomeState home_state_from_string(const char *s);
diff --git a/src/home/homed-manager-bus.c b/src/home/homed-manager-bus.c
new file mode 100644 (file)
index 0000000..a6b30d9
--- /dev/null
@@ -0,0 +1,690 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <linux/capability.h>
+
+#include "alloc-util.h"
+#include "bus-common-errors.h"
+#include "bus-polkit.h"
+#include "format-util.h"
+#include "homed-bus.h"
+#include "homed-home-bus.h"
+#include "homed-manager-bus.h"
+#include "homed-manager.h"
+#include "strv.h"
+#include "user-record-sign.h"
+#include "user-record-util.h"
+#include "user-util.h"
+
+static int property_get_auto_login(
+                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;
+        Iterator i;
+        Home *h;
+        int r;
+
+        assert(bus);
+        assert(reply);
+        assert(m);
+
+        r = sd_bus_message_open_container(reply, 'a', "(sso)");
+        if (r < 0)
+                return r;
+
+        HASHMAP_FOREACH(h, m->homes_by_name, i) {
+                _cleanup_(strv_freep) char **seats = NULL;
+                _cleanup_free_ char *home_path = NULL;
+                char **s;
+
+                r = home_auto_login(h, &seats);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to determine whether home '%s' is candidate for auto-login, ignoring: %m", h->user_name);
+                        continue;
+                }
+                if (!r)
+                        continue;
+
+                r = bus_home_path(h, &home_path);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to generate home bus path: %m");
+
+                STRV_FOREACH(s, seats) {
+                        r = sd_bus_message_append(reply, "(sso)", h->user_name, *s, home_path);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        return sd_bus_message_close_container(reply);
+}
+
+static int method_get_home_by_name(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_free_ char *path = NULL;
+        const char *user_name;
+        Manager *m = userdata;
+        Home *h;
+        int r;
+
+        assert(message);
+        assert(m);
+
+        r = sd_bus_message_read(message, "s", &user_name);
+        if (r < 0)
+                return r;
+        if (!valid_user_group_name(user_name))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
+
+        h = hashmap_get(m->homes_by_name, user_name);
+        if (!h)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", user_name);
+
+        r = bus_home_path(h, &path);
+        if (r < 0)
+                return r;
+
+        return sd_bus_reply_method_return(
+                        message, "usussso",
+                        (uint32_t) h->uid,
+                        home_state_to_string(home_get_state(h)),
+                        h->record ? (uint32_t) user_record_gid(h->record) : GID_INVALID,
+                        h->record ? user_record_real_name(h->record) : NULL,
+                        h->record ? user_record_home_directory(h->record) : NULL,
+                        h->record ? user_record_shell(h->record) : NULL,
+                        path);
+}
+
+static int method_get_home_by_uid(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_free_ char *path = NULL;
+        Manager *m = userdata;
+        uint32_t uid;
+        int r;
+        Home *h;
+
+        assert(message);
+        assert(m);
+
+        r = sd_bus_message_read(message, "u", &uid);
+        if (r < 0)
+                return r;
+        if (!uid_is_valid(uid))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "UID " UID_FMT " is not valid", uid);
+
+        h = hashmap_get(m->homes_by_uid, UID_TO_PTR(uid));
+        if (!h)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for UID " UID_FMT " known", uid);
+
+        /* Note that we don't use bus_home_path() here, but build the path manually, since if we are queried
+         * for a UID we should also generate the bus path with a UID, and bus_home_path() uses our more
+         * typical bus path by name. */
+        if (asprintf(&path, "/org/freedesktop/home1/home/" UID_FMT, h->uid) < 0)
+                return -ENOMEM;
+
+        return sd_bus_reply_method_return(
+                        message, "ssussso",
+                        h->user_name,
+                        home_state_to_string(home_get_state(h)),
+                        h->record ? (uint32_t) user_record_gid(h->record) : GID_INVALID,
+                        h->record ? user_record_real_name(h->record) : NULL,
+                        h->record ? user_record_home_directory(h->record) : NULL,
+                        h->record ? user_record_shell(h->record) : NULL,
+                        path);
+}
+
+static int method_list_homes(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        Manager *m = userdata;
+        Iterator i;
+        Home *h;
+        int r;
+
+        assert(message);
+        assert(m);
+
+        r = sd_bus_message_new_method_return(message, &reply);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_open_container(reply, 'a', "(susussso)");
+        if (r < 0)
+                return r;
+
+        HASHMAP_FOREACH(h, m->homes_by_uid, i) {
+                _cleanup_free_ char *path = NULL;
+
+                r = bus_home_path(h, &path);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_append(
+                                reply, "(susussso)",
+                                h->user_name,
+                                (uint32_t) h->uid,
+                                home_state_to_string(home_get_state(h)),
+                                h->record ? (uint32_t) user_record_gid(h->record) : GID_INVALID,
+                                h->record ? user_record_real_name(h->record) : NULL,
+                                h->record ? user_record_home_directory(h->record) : NULL,
+                                h->record ? user_record_shell(h->record) : NULL,
+                                path);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_bus_message_close_container(reply);
+        if (r < 0)
+                return r;
+
+        return sd_bus_send(NULL, reply, NULL);
+}
+
+static int method_get_user_record_by_name(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_free_ char *json = NULL, *path = NULL;
+        Manager *m = userdata;
+        const char *user_name;
+        bool incomplete;
+        Home *h;
+        int r;
+
+        assert(message);
+        assert(m);
+
+        r = sd_bus_message_read(message, "s", &user_name);
+        if (r < 0)
+                return r;
+        if (!valid_user_group_name(user_name))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
+
+        h = hashmap_get(m->homes_by_name, user_name);
+        if (!h)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", user_name);
+
+        r = bus_home_get_record_json(h, message, &json, &incomplete);
+        if (r < 0)
+                return r;
+
+        r = bus_home_path(h, &path);
+        if (r < 0)
+                return r;
+
+        return sd_bus_reply_method_return(
+                        message, "sbo",
+                        json,
+                        incomplete,
+                        path);
+}
+
+static int method_get_user_record_by_uid(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_free_ char *json = NULL, *path = NULL;
+        Manager *m = userdata;
+        bool incomplete;
+        uint32_t uid;
+        Home *h;
+        int r;
+
+        assert(message);
+        assert(m);
+
+        r = sd_bus_message_read(message, "u", &uid);
+        if (r < 0)
+                return r;
+        if (!uid_is_valid(uid))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "UID " UID_FMT " is not valid", uid);
+
+        h = hashmap_get(m->homes_by_uid, UID_TO_PTR(uid));
+        if (!h)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for UID " UID_FMT " known", uid);
+
+        r = bus_home_get_record_json(h, message, &json, &incomplete);
+        if (r < 0)
+                return r;
+
+        if (asprintf(&path, "/org/freedesktop/home1/home/" UID_FMT, h->uid) < 0)
+                return -ENOMEM;
+
+        return sd_bus_reply_method_return(
+                        message, "sbo",
+                        json,
+                        incomplete,
+                        path);
+}
+
+static int generic_home_method(
+                Manager *m,
+                sd_bus_message *message,
+                sd_bus_message_handler_t handler,
+                sd_bus_error *error) {
+
+        const char *user_name;
+        Home *h;
+        int r;
+
+        r = sd_bus_message_read(message, "s", &user_name);
+        if (r < 0)
+                return r;
+
+        if (!valid_user_group_name(user_name))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name %s is not valid", user_name);
+
+        h = hashmap_get(m->homes_by_name, user_name);
+        if (!h)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", user_name);
+
+        return handler(message, h, error);
+}
+
+static int method_activate_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return generic_home_method(userdata, message, bus_home_method_activate, error);
+}
+
+static int method_deactivate_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return generic_home_method(userdata, message, bus_home_method_deactivate, error);
+}
+
+static int validate_and_allocate_home(Manager *m, UserRecord *hr, Home **ret, sd_bus_error *error) {
+        _cleanup_(user_record_unrefp) UserRecord *signed_hr = NULL;
+        struct passwd *pw;
+        struct group *gr;
+        bool signed_locally;
+        Home *other;
+        int r;
+
+        assert(m);
+        assert(hr);
+        assert(ret);
+
+        r = user_record_is_supported(hr, error);
+        if (r < 0)
+                return r;
+
+        other = hashmap_get(m->homes_by_name, hr->user_name);
+        if (other)
+                return sd_bus_error_setf(error, BUS_ERROR_USER_NAME_EXISTS, "Specified user name %s exists already, refusing.", hr->user_name);
+
+        pw = getpwnam(hr->user_name);
+        if (pw)
+                return sd_bus_error_setf(error, BUS_ERROR_USER_NAME_EXISTS, "Specified user name %s exists in the NSS user database, refusing.", hr->user_name);
+
+        gr = getgrnam(hr->user_name);
+        if (gr)
+                return sd_bus_error_setf(error, BUS_ERROR_USER_NAME_EXISTS, "Specified user name %s conflicts with an NSS group by the same name, refusing.", hr->user_name);
+
+        r = manager_verify_user_record(m, hr);
+        switch (r) {
+
+        case USER_RECORD_UNSIGNED:
+                /* If the record is unsigned, then let's sign it with our own key */
+                r = manager_sign_user_record(m, hr, &signed_hr, error);
+                if (r < 0)
+                        return r;
+
+                hr = signed_hr;
+                _fallthrough_;
+
+        case USER_RECORD_SIGNED_EXCLUSIVE:
+                signed_locally = true;
+                break;
+
+        case USER_RECORD_SIGNED:
+        case USER_RECORD_FOREIGN:
+                signed_locally = false;
+                break;
+
+        case -ENOKEY:
+                return sd_bus_error_setf(error, BUS_ERROR_BAD_SIGNATURE, "Specified user record for %s is signed by a key we don't recognize, refusing.", hr->user_name);
+
+        default:
+                return sd_bus_error_set_errnof(error, r, "Failed to validate signature for '%s': %m", hr->user_name);
+        }
+
+        if (uid_is_valid(hr->uid)) {
+                other = hashmap_get(m->homes_by_uid, UID_TO_PTR(hr->uid));
+                if (other)
+                        return sd_bus_error_setf(error, BUS_ERROR_UID_IN_USE, "Specified UID " UID_FMT " already in use by home %s, refusing.", hr->uid, other->user_name);
+
+                pw = getpwuid(hr->uid);
+                if (pw)
+                        return sd_bus_error_setf(error, BUS_ERROR_UID_IN_USE, "Specified UID " UID_FMT " already in use by NSS user %s, refusing.", hr->uid, pw->pw_name);
+
+                gr = getgrgid(hr->uid);
+                if (gr)
+                        return sd_bus_error_setf(error, BUS_ERROR_UID_IN_USE, "Specified UID " UID_FMT " already in use as GID by NSS group %s, refusing.", hr->uid, gr->gr_name);
+        } else {
+                r = manager_augment_record_with_uid(m, hr);
+                if (r < 0)
+                        return sd_bus_error_set_errnof(error, r, "Failed to acquire UID for '%s': %m", hr->user_name);
+        }
+
+        r = home_new(m, hr, NULL, ret);
+        if (r < 0)
+                return r;
+
+        (*ret)->signed_locally = signed_locally;
+        return r;
+}
+
+static int method_register_home(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+        Manager *m = userdata;
+        Home *h;
+        int r;
+
+        assert(message);
+        assert(m);
+
+        r = bus_message_read_home_record(message, USER_RECORD_LOAD_EMBEDDED, &hr, error);
+        if (r < 0)
+                return r;
+
+        r = bus_verify_polkit_async(
+                        message,
+                        CAP_SYS_ADMIN,
+                        "org.freedesktop.home1.create-home",
+                        NULL,
+                        true,
+                        UID_INVALID,
+                        &m->polkit_registry,
+                        error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* Will call us back */
+
+        r = validate_and_allocate_home(m, hr, &h, error);
+        if (r < 0)
+                return r;
+
+        r = home_save_record(h);
+        if (r < 0) {
+                home_free(h);
+                return r;
+        }
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
+static int method_unregister_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return generic_home_method(userdata, message, bus_home_method_unregister, error);
+}
+
+static int method_create_home(
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+        Manager *m = userdata;
+        Home *h;
+        int r;
+
+        assert(message);
+        assert(m);
+
+        r = bus_message_read_home_record(message, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE, &hr, error);
+        if (r < 0)
+                return r;
+
+        r = bus_verify_polkit_async(
+                        message,
+                        CAP_SYS_ADMIN,
+                        "org.freedesktop.home1.create-home",
+                        NULL,
+                        true,
+                        UID_INVALID,
+                        &m->polkit_registry,
+                        error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* Will call us back */
+
+        r = validate_and_allocate_home(m, hr, &h, error);
+        if (r < 0)
+                return r;
+
+        r = home_create(h, hr, error);
+        if (r < 0)
+                goto fail;
+
+        assert(r == 0);
+        h->unregister_on_failure = true;
+        assert(!h->current_operation);
+
+        r = home_set_current_message(h, message);
+        if (r < 0)
+                return r;
+
+        return 1;
+
+fail:
+        (void) home_unlink_record(h);
+        h = home_free(h);
+        return r;
+}
+
+static int method_realize_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return generic_home_method(userdata, message, bus_home_method_realize, error);
+}
+
+static int method_remove_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return generic_home_method(userdata, message, bus_home_method_remove, error);
+}
+
+static int method_fixate_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return generic_home_method(userdata, message, bus_home_method_fixate, error);
+}
+
+static int method_authenticate_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return generic_home_method(userdata, message, bus_home_method_authenticate, error);
+}
+
+static int method_update_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+        Manager *m = userdata;
+        Home *h;
+        int r;
+
+        assert(message);
+        assert(m);
+
+        r = bus_message_read_home_record(message, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE, &hr, error);
+        if (r < 0)
+                return r;
+
+        assert(hr->user_name);
+
+        h = hashmap_get(m->homes_by_name, hr->user_name);
+        if (!h)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_HOME, "No home for user %s known", hr->user_name);
+
+        return bus_home_method_update_record(h, message, hr, error);
+}
+
+static int method_resize_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return generic_home_method(userdata, message, bus_home_method_resize, error);
+}
+
+static int method_change_password_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return generic_home_method(userdata, message, bus_home_method_change_password, error);
+}
+
+static int method_lock_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return generic_home_method(userdata, message, bus_home_method_lock, error);
+}
+
+static int method_unlock_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return generic_home_method(userdata, message, bus_home_method_unlock, error);
+}
+
+static int method_acquire_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return generic_home_method(userdata, message, bus_home_method_acquire, error);
+}
+
+static int method_ref_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return generic_home_method(userdata, message, bus_home_method_ref, error);
+}
+
+static int method_release_home(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return generic_home_method(userdata, message, bus_home_method_release, error);
+}
+
+static int method_lock_all_homes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        _cleanup_(operation_unrefp) Operation *o = NULL;
+        bool waiting = false;
+        Manager *m = userdata;
+        Iterator i;
+        Home *h;
+        int r;
+
+        assert(m);
+
+        /* This is called from logind when we are preparing for system suspend. We enqueue a lock operation
+         * for every suitable home we have and only when all of them completed we send a reply indicating
+         * completion. */
+
+        HASHMAP_FOREACH(h, m->homes_by_name, i) {
+
+                /* Automatically suspend all homes that have at least one client referencing it that asked
+                 * for "please suspend", and no client that asked for "please do not suspend". */
+                if (h->ref_event_source_dont_suspend ||
+                    !h->ref_event_source_please_suspend)
+                        continue;
+
+                if (!o) {
+                        o = operation_new(OPERATION_LOCK_ALL, message);
+                        if (!o)
+                                return -ENOMEM;
+                }
+
+                log_info("Automatically locking of home of user %s.", h->user_name);
+
+                r = home_schedule_operation(h, o, error);
+                if (r < 0)
+                        return r;
+
+                waiting = true;
+        }
+
+        if (waiting) /* At least one lock operation was enqeued, let's leave here without a reply: it will
+                        * be sent as soon as the last of the lock operations completed. */
+                return 1;
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
+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
+         * 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),
+
+        /* An operation that acts on all homes that allow it */
+        SD_BUS_METHOD("LockAllHomes", NULL, NULL, method_lock_all_homes, 0),
+
+        SD_BUS_VTABLE_END
+};
+
+static int on_deferred_auto_login(sd_event_source *s, void *userdata) {
+        Manager *m = userdata;
+        int r;
+
+        assert(m);
+
+        m->deferred_auto_login_event_source = sd_event_source_unref(m->deferred_auto_login_event_source);
+
+        r = sd_bus_emit_properties_changed(
+                        m->bus,
+                        "/org/freedesktop/home1",
+                        "org.freedesktop.home1.Manager",
+                        "AutoLogin", NULL);
+        if (r < 0)
+                log_warning_errno(r, "Failed to send AutoLogin property change event, ignoring: %m");
+
+        return 0;
+}
+
+int bus_manager_emit_auto_login_changed(Manager *m) {
+        int r;
+        assert(m);
+
+        if (m->deferred_auto_login_event_source)
+                return 0;
+
+        if (!m->event)
+                return 0;
+
+        if (IN_SET(sd_event_get_state(m->event), SD_EVENT_FINISHED, SD_EVENT_EXITING))
+                return 0;
+
+        r = sd_event_add_defer(m->event, &m->deferred_auto_login_event_source, on_deferred_auto_login, m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate auto login event source: %m");
+
+        r = sd_event_source_set_priority(m->deferred_auto_login_event_source, SD_EVENT_PRIORITY_IDLE+10);
+        if (r < 0)
+                log_warning_errno(r, "Failed to tweak priority of event source, ignoring: %m");
+
+        (void) sd_event_source_set_description(m->deferred_auto_login_event_source, "deferred-auto-login");
+        return 1;
+}
diff --git a/src/home/homed-manager-bus.h b/src/home/homed-manager-bus.h
new file mode 100644 (file)
index 0000000..40e1cc3
--- /dev/null
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+extern const sd_bus_vtable manager_vtable[];
diff --git a/src/home/homed-manager.c b/src/home/homed-manager.c
new file mode 100644 (file)
index 0000000..5d77760
--- /dev/null
@@ -0,0 +1,1671 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <grp.h>
+#include <linux/fs.h>
+#include <linux/magic.h>
+#include <openssl/pem.h>
+#include <pwd.h>
+#include <sys/ioctl.h>
+#include <sys/quota.h>
+#include <sys/stat.h>
+
+#include "btrfs-util.h"
+#include "bus-common-errors.h"
+#include "bus-error.h"
+#include "bus-polkit.h"
+#include "clean-ipc.h"
+#include "conf-files.h"
+#include "device-util.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "format-util.h"
+#include "fs-util.h"
+#include "gpt.h"
+#include "home-util.h"
+#include "homed-home-bus.h"
+#include "homed-home.h"
+#include "homed-manager-bus.h"
+#include "homed-manager.h"
+#include "homed-varlink.h"
+#include "io-util.h"
+#include "mkdir.h"
+#include "process-util.h"
+#include "quota-util.h"
+#include "random-util.h"
+#include "socket-util.h"
+#include "stat-util.h"
+#include "strv.h"
+#include "tmpfile-util.h"
+#include "udev-util.h"
+#include "user-record-sign.h"
+#include "user-record-util.h"
+#include "user-record.h"
+#include "user-util.h"
+
+/* Where to look for private/public keys that are used to sign the user records. We are not using
+ * CONF_PATHS_NULSTR() here since we want to insert /var/lib/systemd/home/ in the middle. And we insert that
+ * since we want to auto-generate a persistent private/public key pair if we need to. */
+#define KEY_PATHS_NULSTR                        \
+        "/etc/systemd/home/\0"                  \
+        "/run/systemd/home/\0"                  \
+        "/var/lib/systemd/home/\0"              \
+        "/usr/local/lib/systemd/home/\0"        \
+        "/usr/lib/systemd/home/\0"
+
+static bool uid_is_home(uid_t uid) {
+        return uid >= HOME_UID_MIN && uid <= HOME_UID_MAX;
+}
+/* Takes a value generated randomly or by hashing and turns it into a UID in the right range */
+
+#define UID_CLAMP_INTO_HOME_RANGE(rnd) (((uid_t) (rnd) % (HOME_UID_MAX - HOME_UID_MIN + 1)) + HOME_UID_MIN)
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(homes_by_uid_hash_ops, void, trivial_hash_func, trivial_compare_func, Home, home_free);
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(homes_by_name_hash_ops, char, string_hash_func, string_compare_func, Home, home_free);
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(homes_by_worker_pid_hash_ops, void, trivial_hash_func, trivial_compare_func, Home, home_free);
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(homes_by_sysfs_hash_ops, char, path_hash_func, path_compare, Home, home_free);
+
+static int on_home_inotify(sd_event_source *s, const struct inotify_event *event, void *userdata);
+static int manager_gc_images(Manager *m);
+static int manager_enumerate_images(Manager *m);
+static int manager_assess_image(Manager *m, int dir_fd, const char *dir_path, const char *dentry_name);
+static void manager_revalidate_image(Manager *m, Home *h);
+
+static void manager_watch_home(Manager *m) {
+        struct statfs sfs;
+        int r;
+
+        assert(m);
+
+        m->inotify_event_source = sd_event_source_unref(m->inotify_event_source);
+        m->scan_slash_home = false;
+
+        if (statfs("/home/", &sfs) < 0) {
+                log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
+                               "Failed to statfs() /home/ directory, disabling automatic scanning.");
+                return;
+        }
+
+        if (is_network_fs(&sfs)) {
+                log_info("/home/ is a network file system, disabling automatic scanning.");
+                return;
+        }
+
+        if (is_fs_type(&sfs, AUTOFS_SUPER_MAGIC)) {
+                log_info("/home/ is on autofs, disabling automatic scanning.");
+                return;
+        }
+
+        m->scan_slash_home = true;
+
+        r = sd_event_add_inotify(m->event, &m->inotify_event_source, "/home/", IN_CREATE|IN_CLOSE_WRITE|IN_DELETE_SELF|IN_MOVE_SELF|IN_ONLYDIR|IN_MOVED_TO|IN_MOVED_FROM|IN_DELETE, on_home_inotify, m);
+        if (r < 0)
+                log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
+                               "Failed to create inotify watch on /home/, ignoring.");
+
+        (void) sd_event_source_set_description(m->inotify_event_source, "home-inotify");
+}
+
+static int on_home_inotify(sd_event_source *s, const struct inotify_event *event, void *userdata) {
+        Manager *m = userdata;
+        const char *e, *n;
+
+        assert(m);
+        assert(event);
+
+        if ((event->mask & (IN_Q_OVERFLOW|IN_MOVE_SELF|IN_DELETE_SELF|IN_IGNORED|IN_UNMOUNT)) != 0) {
+
+                if (FLAGS_SET(event->mask, IN_Q_OVERFLOW))
+                        log_debug("/home/ inotify queue overflow, rescanning.");
+                else if (FLAGS_SET(event->mask, IN_MOVE_SELF))
+                        log_info("/home/ moved or renamed, recreating watch and rescanning.");
+                else if (FLAGS_SET(event->mask, IN_DELETE_SELF))
+                        log_info("/home/ deleted, recreating watch and rescanning.");
+                else if (FLAGS_SET(event->mask, IN_UNMOUNT))
+                        log_info("/home/ unmounted, recreating watch and rescanning.");
+                else if (FLAGS_SET(event->mask, IN_IGNORED))
+                        log_info("/home/ watch invalidated, recreating watch and rescanning.");
+
+                manager_watch_home(m);
+                (void) manager_gc_images(m);
+                (void) manager_enumerate_images(m);
+                (void) bus_manager_emit_auto_login_changed(m);
+                return 0;
+        }
+
+        /* For the other inotify events, let's ignore all events for file names that don't match our
+         * expectations */
+        if (isempty(event->name))
+                return 0;
+        e = endswith(event->name, FLAGS_SET(event->mask, IN_ISDIR) ? ".homedir" : ".home");
+        if (!e)
+                return 0;
+
+        n = strndupa(event->name, e - event->name);
+        if (!suitable_user_name(n))
+                return 0;
+
+        if ((event->mask & (IN_CREATE|IN_CLOSE_WRITE|IN_MOVED_TO)) != 0) {
+                if (FLAGS_SET(event->mask, IN_CREATE))
+                        log_debug("/home/%s has been created, having a look.", event->name);
+                else if (FLAGS_SET(event->mask, IN_CLOSE_WRITE))
+                        log_debug("/home/%s has been modified, having a look.", event->name);
+                else if (FLAGS_SET(event->mask, IN_MOVED_TO))
+                        log_debug("/home/%s has been moved in, having a look.", event->name);
+
+                (void) manager_assess_image(m, -1, "/home/", event->name);
+                (void) bus_manager_emit_auto_login_changed(m);
+        }
+
+        if ((event->mask & (IN_DELETE|IN_MOVED_FROM|IN_DELETE)) != 0) {
+                Home *h;
+
+                if (FLAGS_SET(event->mask, IN_DELETE))
+                        log_debug("/home/%s has been deleted, revalidating.", event->name);
+                else if (FLAGS_SET(event->mask, IN_CLOSE_WRITE))
+                        log_debug("/home/%s has been closed after writing, revalidating.", event->name);
+                else if (FLAGS_SET(event->mask, IN_MOVED_FROM))
+                        log_debug("/home/%s has been moved away, revalidating.", event->name);
+
+                h = hashmap_get(m->homes_by_name, n);
+                if (h) {
+                        manager_revalidate_image(m, h);
+                        (void) bus_manager_emit_auto_login_changed(m);
+                }
+        }
+
+        return 0;
+}
+
+int manager_new(Manager **ret) {
+        _cleanup_(manager_freep) Manager *m = NULL;
+        int r;
+
+        assert(ret);
+
+        m = new0(Manager, 1);
+        if (!m)
+                return -ENOMEM;
+
+        r = sd_event_default(&m->event);
+        if (r < 0)
+                return r;
+
+        r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
+        if (r < 0)
+                return r;
+
+        r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
+        if (r < 0)
+                return r;
+
+        (void) sd_event_set_watchdog(m->event, true);
+
+        m->homes_by_uid = hashmap_new(&homes_by_uid_hash_ops);
+        if (!m->homes_by_uid)
+                return -ENOMEM;
+
+        m->homes_by_name = hashmap_new(&homes_by_name_hash_ops);
+        if (!m->homes_by_name)
+                return -ENOMEM;
+
+        m->homes_by_worker_pid = hashmap_new(&homes_by_worker_pid_hash_ops);
+        if (!m->homes_by_worker_pid)
+                return -ENOMEM;
+
+        m->homes_by_sysfs = hashmap_new(&homes_by_sysfs_hash_ops);
+        if (!m->homes_by_sysfs)
+                return -ENOMEM;
+
+        *ret = TAKE_PTR(m);
+        return 0;
+}
+
+Manager* manager_free(Manager *m) {
+        assert(m);
+
+        hashmap_free(m->homes_by_uid);
+        hashmap_free(m->homes_by_name);
+        hashmap_free(m->homes_by_worker_pid);
+        hashmap_free(m->homes_by_sysfs);
+
+        m->inotify_event_source = sd_event_source_unref(m->inotify_event_source);
+
+        bus_verify_polkit_async_registry_free(m->polkit_registry);
+
+        sd_bus_flush_close_unref(m->bus);
+        sd_event_unref(m->event);
+
+        m->notify_socket_event_source = sd_event_source_unref(m->notify_socket_event_source);
+        m->device_monitor = sd_device_monitor_unref(m->device_monitor);
+
+        m->deferred_rescan_event_source = sd_event_source_unref(m->deferred_rescan_event_source);
+        m->deferred_gc_event_source = sd_event_source_unref(m->deferred_gc_event_source);
+        m->deferred_auto_login_event_source = sd_event_source_unref(m->deferred_auto_login_event_source);
+
+        if (m->private_key)
+                EVP_PKEY_free(m->private_key);
+
+        hashmap_free(m->public_keys);
+
+        varlink_server_unref(m->varlink_server);
+
+        return mfree(m);
+}
+
+int manager_verify_user_record(Manager *m, UserRecord *hr) {
+        EVP_PKEY *pkey;
+        Iterator i;
+        int r;
+
+        assert(m);
+        assert(hr);
+
+        if (!m->private_key && hashmap_isempty(m->public_keys)) {
+                r = user_record_has_signature(hr);
+                if (r < 0)
+                        return r;
+
+                return r ? -ENOKEY : USER_RECORD_UNSIGNED;
+        }
+
+        /* Is it our own? */
+        if (m->private_key) {
+                r = user_record_verify(hr, m->private_key);
+                switch (r) {
+
+                case USER_RECORD_FOREIGN:
+                        /* This record is not signed by this key, but let's see below */
+                        break;
+
+                case USER_RECORD_SIGNED:               /* Signed by us, but also by others, let's propagate that */
+                case USER_RECORD_SIGNED_EXCLUSIVE:     /* Signed by us, and nothing else, ditto */
+                case USER_RECORD_UNSIGNED:             /* Not signed at all, ditto  */
+                default:
+                        return r;
+                }
+        }
+
+        HASHMAP_FOREACH(pkey, m->public_keys, i) {
+                r = user_record_verify(hr, pkey);
+                switch (r) {
+
+                case USER_RECORD_FOREIGN:
+                        /* This record is not signed by this key, but let's see our other keys */
+                        break;
+
+                case USER_RECORD_SIGNED:            /* It's signed by this key we are happy with, but which is not our own. */
+                case USER_RECORD_SIGNED_EXCLUSIVE:
+                        return USER_RECORD_FOREIGN;
+
+                case USER_RECORD_UNSIGNED: /* It's not signed at all */
+                default:
+                        return r;
+                }
+        }
+
+        return -ENOKEY;
+}
+
+static int manager_add_home_by_record(
+                Manager *m,
+                const char *name,
+                int dir_fd,
+                const char *fname) {
+
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+        unsigned line, column;
+        int r, is_signed;
+        Home *h;
+
+        assert(m);
+        assert(name);
+        assert(fname);
+
+        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);
+
+        hr = user_record_new();
+        if (!hr)
+                return log_oom();
+
+        r = user_record_load(hr, v, USER_RECORD_LOAD_REFUSE_SECRET);
+        if (r < 0)
+                return r;
+
+        if (!streq_ptr(hr->user_name, name))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Identity's user name %s does not match file name %s, refusing.", hr->user_name, name);
+
+        is_signed = manager_verify_user_record(m, hr);
+        switch (is_signed) {
+
+        case -ENOKEY:
+                return log_warning_errno(is_signed, "User record %s is not signed by any accepted key, ignoring.", fname);
+        case USER_RECORD_UNSIGNED:
+                return log_warning_errno(SYNTHETIC_ERRNO(EPERM), "User record %s is not signed at all, ignoring.", fname);
+        case USER_RECORD_SIGNED:
+                log_info("User record %s is signed by us (and others), accepting.", fname);
+                break;
+        case USER_RECORD_SIGNED_EXCLUSIVE:
+                log_info("User record %s is signed only by us, accepting.", fname);
+                break;
+        case USER_RECORD_FOREIGN:
+                log_info("User record %s is signed by registered key from others, accepting.", fname);
+                break;
+        default:
+                assert(is_signed < 0);
+                return log_error_errno(is_signed, "Failed to verify signature of user record in %s: %m", fname);
+        }
+
+        h = hashmap_get(m->homes_by_name, name);
+        if (h) {
+                r = home_set_record(h, hr);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to update home record for %s: %m", name);
+
+                /* 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. */
+                if (h->state == HOME_UNFIXATED)
+                        h->state = _HOME_STATE_INVALID;
+        } else {
+                r = home_new(m, hr, NULL, &h);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to allocate new home object: %m");
+
+                log_info("Added registered home for user %s.", hr->user_name);
+        }
+
+        /* Only entries we exclusively signed are writable to us, hence remember the result */
+        h->signed_locally = is_signed == USER_RECORD_SIGNED_EXCLUSIVE;
+
+        return 1;
+}
+
+static int manager_enumerate_records(Manager *m) {
+        _cleanup_closedir_ DIR *d = NULL;
+        struct dirent *de;
+
+        assert(m);
+
+        d = opendir("/var/lib/systemd/home/");
+        if (!d)
+                return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno,
+                                      "Failed to open /var/lib/systemd/home/: %m");
+
+        FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read record directory: %m")) {
+                _cleanup_free_ char *n = NULL;
+                const char *e;
+
+                if (!dirent_is_file(de))
+                        continue;
+
+                e = endswith(de->d_name, ".identity");
+                if (!e)
+                        continue;
+
+                n = strndup(de->d_name, e - de->d_name);
+                if (!n)
+                        return log_oom();
+
+                if (!suitable_user_name(n))
+                        continue;
+
+                (void) manager_add_home_by_record(m, n, dirfd(d), de->d_name);
+        }
+
+        return 0;
+}
+
+static int search_quota(uid_t uid, const char *exclude_quota_path) {
+        struct stat exclude_st = {};
+        dev_t previous_devno = 0;
+        const char *where;
+        int r;
+
+        /* Checks whether the specified UID owns any files on the files system, but ignore any file system
+         * backing the specified file. The file is used when operating on home directories, where it's OK if
+         * the UID of them already owns files. */
+
+        if (exclude_quota_path && stat(exclude_quota_path, &exclude_st) < 0) {
+                if (errno != ENOENT)
+                        return log_warning_errno(errno, "Failed to stat %s, ignoring: %m", exclude_quota_path);
+        }
+
+        /* Check a few usual suspects where regular users might own files. Note that this is by no means
+         * comprehensive, but should cover most cases. Note that in an ideal world every user would be
+         * registered in NSS and avoid our own UID range, but for all other cases, it's a good idea to be
+         * paranoid and check quota if we can. */
+        FOREACH_STRING(where, "/home/", "/tmp/", "/var/", "/var/mail/", "/var/tmp/", "/var/spool/") {
+                struct dqblk req;
+                struct stat st;
+
+                if (stat(where, &st) < 0) {
+                        log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno,
+                                       "Failed to stat %s, ignoring: %m", where);
+                        continue;
+                }
+
+                if (major(st.st_dev) == 0) {
+                        log_debug("Directory %s is not on a real block device, not checking quota for UID use.", where);
+                        continue;
+                }
+
+                if (st.st_dev == exclude_st.st_dev) { /* If an exclude path is specified, then ignore quota
+                                                       * reported on the same block device as that path. */
+                        log_debug("Directory %s is where the home directory is located, not checking quota for UID use.", where);
+                        continue;
+                }
+
+                if (st.st_dev == previous_devno) { /* Does this directory have the same devno as the previous
+                                                    * one we tested? If so, there's no point in testing this
+                                                    * again. */
+                        log_debug("Directory %s is on same device as previous tested directory, not checking quota for UID use a second time.", where);
+                        continue;
+                }
+
+                previous_devno = st.st_dev;
+
+                r = quotactl_devno(QCMD_FIXED(Q_GETQUOTA, USRQUOTA), st.st_dev, uid, &req);
+                if (r < 0) {
+                        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);
+
+                        continue;
+                }
+
+                if ((FLAGS_SET(req.dqb_valid, QIF_SPACE) && req.dqb_curspace > 0) ||
+                    (FLAGS_SET(req.dqb_valid, QIF_INODES) && req.dqb_curinodes > 0)) {
+                        log_debug_errno(errno, "Quota reports UID " UID_FMT " occupies disk space on %s.", uid, where);
+                        return 1;
+                }
+        }
+
+        return 0;
+}
+
+static int manager_acquire_uid(
+                Manager *m,
+                uid_t start_uid,
+                const char *user_name,
+                const char *exclude_quota_path,
+                uid_t *ret) {
+
+        static const uint8_t hash_key[] = {
+                0xa3, 0xb8, 0x82, 0x69, 0x9a, 0x71, 0xf7, 0xa9,
+                0xe0, 0x7c, 0xf6, 0xf1, 0x21, 0x69, 0xd2, 0x1e
+        };
+
+        enum {
+                PHASE_SUGGESTED,
+                PHASE_HASHED,
+                PHASE_RANDOM
+        } phase = PHASE_SUGGESTED;
+
+        unsigned n_tries = 100;
+        int r;
+
+        assert(m);
+        assert(ret);
+
+        for (;;) {
+                struct passwd *pw;
+                struct group *gr;
+                uid_t candidate;
+                Home *other;
+
+                if (--n_tries <= 0)
+                        return -EBUSY;
+
+                switch (phase) {
+
+                case PHASE_SUGGESTED:
+                        phase = PHASE_HASHED;
+
+                        if (!uid_is_home(start_uid))
+                                continue;
+
+                        candidate = start_uid;
+                        break;
+
+                case PHASE_HASHED:
+                        phase = PHASE_RANDOM;
+
+                        if (!user_name)
+                                continue;
+
+                        candidate = UID_CLAMP_INTO_HOME_RANGE(siphash24(user_name, strlen(user_name), hash_key));
+                        break;
+
+                case PHASE_RANDOM:
+                        random_bytes(&candidate, sizeof(candidate));
+                        candidate = UID_CLAMP_INTO_HOME_RANGE(candidate);
+                        break;
+
+                default:
+                        assert_not_reached("unknown phase");
+                }
+
+                other = hashmap_get(m->homes_by_uid, UID_TO_PTR(candidate));
+                if (other) {
+                        log_debug("Candidate UID " UID_FMT " already used by another home directory (%s), let's try another.", candidate, other->user_name);
+                        continue;
+                }
+
+                pw = getpwuid(candidate);
+                if (pw) {
+                        log_debug("Candidate UID " UID_FMT " already registered by another user in NSS (%s), let's try another.", candidate, pw->pw_name);
+                        continue;
+                }
+
+                gr = getgrgid((gid_t) candidate);
+                if (gr) {
+                        log_debug("Candidate UID " UID_FMT " already registered by another group in NSS (%s), let's try another.", candidate, gr->gr_name);
+                        continue;
+                }
+
+                r = search_ipc(candidate, (gid_t) candidate);
+                if (r < 0)
+                        continue;
+                if (r > 0) {
+                        log_debug_errno(r, "Candidate UID " UID_FMT " already owns IPC objects, let's try another: %m", candidate);
+                        continue;
+                }
+
+                r = search_quota(candidate, exclude_quota_path);
+                if (r != 0)
+                        continue;
+
+                *ret = candidate;
+                return 0;
+        }
+}
+
+static int manager_add_home_by_image(
+                Manager *m,
+                const char *user_name,
+                const char *realm,
+                const char *image_path,
+                const char *sysfs,
+                UserStorage storage,
+                uid_t start_uid) {
+
+        _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+        uid_t uid;
+        Home *h;
+        int r;
+
+        assert(m);
+
+        assert(m);
+        assert(user_name);
+        assert(image_path);
+        assert(storage >= 0);
+        assert(storage < _USER_STORAGE_MAX);
+
+        h = hashmap_get(m->homes_by_name, user_name);
+        if (h) {
+                bool same;
+
+                if (h->state != HOME_UNFIXATED) {
+                        log_debug("Found an image for user %s which already has a record, skipping.", user_name);
+                        return 0; /* ignore images that synthesize a user we already have a record for */
+                }
+
+                same = user_record_storage(h->record) == storage;
+                if (same) {
+                        if (h->sysfs && sysfs)
+                                same = path_equal(h->sysfs, sysfs);
+                        else if (!!h->sysfs != !!sysfs)
+                                same = false;
+                        else {
+                                const char *p;
+
+                                p = user_record_image_path(h->record);
+                                same = p && path_equal(p, image_path);
+                        }
+                }
+
+                if (!same) {
+                        log_debug("Found a multiple images for a user '%s', ignoring image '%s'.", user_name, image_path);
+                        return 0;
+                }
+        } else {
+                /* Check NSS, in case there's another user or group by this name */
+                if (getpwnam(user_name) || getgrnam(user_name)) {
+                        log_debug("Found an existing user or group by name '%s', ignoring image '%s'.", user_name, image_path);
+                        return 0;
+                }
+        }
+
+        if (h && uid_is_valid(h->uid))
+                uid = h->uid;
+        else {
+                r = manager_acquire_uid(m, start_uid, user_name, IN_SET(storage, USER_SUBVOLUME, USER_DIRECTORY, USER_FSCRYPT) ? image_path : NULL, &uid);
+                if (r < 0)
+                        return log_warning_errno(r, "Failed to acquire unused UID for %s: %m", user_name);
+        }
+
+        hr = user_record_new();
+        if (!hr)
+                return log_oom();
+
+        r = user_record_synthesize(hr, user_name, realm, image_path, storage, uid, (gid_t) uid);
+        if (r < 0)
+                return log_error_errno(r, "Failed to synthesize home record for %s (image %s): %m", user_name, image_path);
+
+        if (h) {
+                r = home_set_record(h, hr);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to update home record for %s: %m", user_name);
+        } else {
+                r = home_new(m, hr, sysfs, &h);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to allocate new home object: %m");
+
+                h->state = HOME_UNFIXATED;
+
+                log_info("Discovered new home for user %s through image %s.", user_name, image_path);
+        }
+
+        return 1;
+}
+
+int manager_augment_record_with_uid(
+                Manager *m,
+                UserRecord *hr) {
+
+        const char *exclude_quota_path = NULL;
+        uid_t start_uid = UID_INVALID, uid;
+        int r;
+
+        assert(m);
+        assert(hr);
+
+        if (uid_is_valid(hr->uid))
+                return 0;
+
+        if (IN_SET(hr->storage, USER_CLASSIC, USER_SUBVOLUME, USER_DIRECTORY, USER_FSCRYPT)) {
+                const char * ip;
+
+                ip = user_record_image_path(hr);
+                if (ip) {
+                        struct stat st;
+
+                        if (stat(ip, &st) < 0) {
+                                if (errno != ENOENT)
+                                        log_warning_errno(errno, "Failed to stat(%s): %m", ip);
+                        }  else if (uid_is_home(st.st_uid)) {
+                                start_uid = st.st_uid;
+                                exclude_quota_path = ip;
+                        }
+                }
+        }
+
+        r = manager_acquire_uid(m, start_uid, hr->user_name, exclude_quota_path, &uid);
+        if (r < 0)
+                return r;
+
+        log_debug("Acquired new UID " UID_FMT " for %s.", uid, hr->user_name);
+
+        r = user_record_add_binding(
+                        hr,
+                        _USER_STORAGE_INVALID,
+                        NULL,
+                        SD_ID128_NULL,
+                        SD_ID128_NULL,
+                        SD_ID128_NULL,
+                        NULL,
+                        NULL,
+                        UINT64_MAX,
+                        NULL,
+                        NULL,
+                        uid,
+                        (gid_t) uid);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+static int manager_assess_image(
+                Manager *m,
+                int dir_fd,
+                const char *dir_path,
+                const char *dentry_name) {
+
+        char *luks_suffix, *directory_suffix;
+        _cleanup_free_ char *path = NULL;
+        struct stat st;
+        int r;
+
+        assert(m);
+        assert(dir_path);
+        assert(dentry_name);
+
+        luks_suffix = endswith(dentry_name, ".home");
+        if (luks_suffix)
+                directory_suffix = NULL;
+        else
+                directory_suffix = endswith(dentry_name, ".homedir");
+
+        /* Early filter out: by name */
+        if (!luks_suffix && !directory_suffix)
+                return 0;
+
+        path = path_join(dir_path, dentry_name);
+        if (!path)
+                return log_oom();
+
+        /* Follow symlinks here, to allow people to link in stuff to make them available locally. */
+        if (dir_fd >= 0)
+                r = fstatat(dir_fd, dentry_name, &st, 0);
+        else
+                r = stat(path, &st);
+        if (r < 0)
+                return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
+                                      "Failed to stat directory entry '%s', ignoring: %m", dentry_name);
+
+        if (S_ISREG(st.st_mode)) {
+                _cleanup_free_ char *n = NULL, *user_name = NULL, *realm = NULL;
+
+                if (!luks_suffix)
+                        return 0;
+
+                n = strndup(dentry_name, luks_suffix - dentry_name);
+                if (!n)
+                        return log_oom();
+
+                r = split_user_name_realm(n, &user_name, &realm);
+                if (r == -EINVAL) /* Not the right format: ignore */
+                        return 0;
+                if (r < 0)
+                        return log_error_errno(r, "Failed to split image name into user name/realm: %m");
+
+                return manager_add_home_by_image(m, user_name, realm, path, NULL, USER_LUKS, UID_INVALID);
+        }
+
+        if (S_ISDIR(st.st_mode)) {
+                _cleanup_free_ char *n = NULL, *user_name = NULL, *realm = NULL;
+                _cleanup_close_ int fd = -1;
+                UserStorage storage;
+
+                if (!directory_suffix)
+                        return 0;
+
+                n = strndup(dentry_name, directory_suffix - dentry_name);
+                if (!n)
+                        return log_oom();
+
+                r = split_user_name_realm(n, &user_name, &realm);
+                if (r == -EINVAL) /* Not the right format: ignore */
+                        return 0;
+                if (r < 0)
+                        return log_error_errno(r, "Failed to split image name into user name/realm: %m");
+
+                if (dir_fd >= 0)
+                        fd = openat(dir_fd, dentry_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
+                else
+                        fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
+                if (fd < 0)
+                        return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
+                                              "Failed to open directory '%s', ignoring: %m", path);
+
+                if (fstat(fd, &st) < 0)
+                        return log_warning_errno(errno, "Failed to fstat() %s, ignoring: %m", path);
+
+                assert(S_ISDIR(st.st_mode)); /* Must hold, we used O_DIRECTORY above */
+
+                r = btrfs_is_subvol_fd(fd);
+                if (r < 0)
+                        return log_warning_errno(errno, "Failed to determine whether %s is a btrfs subvolume: %m", path);
+                if (r > 0)
+                        storage = USER_SUBVOLUME;
+                else {
+                        struct fscrypt_policy policy;
+
+                        if (ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY, &policy) < 0) {
+
+                                if (errno == ENODATA)
+                                        log_debug_errno(errno, "Determined %s is not fscrypt encrypted.", path);
+                                else if (ERRNO_IS_NOT_SUPPORTED(errno))
+                                        log_debug_errno(errno, "Determined %s is not fscrypt encrypted because kernel or file system don't support it.", path);
+                                else
+                                        log_debug_errno(errno, "FS_IOC_GET_ENCRYPTION_POLICY failed with unexpected error code on %s, ignoring: %m", path);
+
+                                storage = USER_DIRECTORY;
+                        } else
+                                storage = USER_FSCRYPT;
+                }
+
+                return manager_add_home_by_image(m, user_name, realm, path, NULL, storage, st.st_uid);
+        }
+
+        return 0;
+}
+
+int manager_enumerate_images(Manager *m) {
+        _cleanup_closedir_ DIR *d = NULL;
+        struct dirent *de;
+
+        assert(m);
+
+        if (!m->scan_slash_home)
+                return 0;
+
+        d = opendir("/home/");
+        if (!d)
+                return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno,
+                                      "Failed to open /home/: %m");
+
+        FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read /home/ directory: %m"))
+                (void) manager_assess_image(m, dirfd(d), "/home", de->d_name);
+
+        return 0;
+}
+
+static int manager_connect_bus(Manager *m) {
+        int r;
+
+        assert(m);
+        assert(!m->bus);
+
+        r = sd_bus_default_system(&m->bus);
+        if (r < 0)
+                return log_error_errno(r, "Failed to connect to system bus: %m");
+
+        r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/home1", "org.freedesktop.home1.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/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");
+
+        r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.home1", 0, NULL, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to request name: %m");
+
+        r = sd_bus_attach_event(m->bus, m->event, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to attach bus to event loop: %m");
+
+        (void) sd_bus_set_exit_on_disconnect(m->bus, true);
+
+        return 0;
+}
+
+static int manager_bind_varlink(Manager *m) {
+        int r;
+
+        assert(m);
+        assert(!m->varlink_server);
+
+        r = varlink_server_new(&m->varlink_server, VARLINK_SERVER_ACCOUNT_UID);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate varlink server object: %m");
+
+        varlink_server_set_userdata(m->varlink_server, m);
+
+        r = varlink_server_bind_method_many(
+                        m->varlink_server,
+                        "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(m->varlink_server, "/run/systemd/userdb/io.systemd.Home", 0666);
+        if (r < 0)
+                return log_error_errno(r, "Failed to bind to varlink socket: %m");
+
+        r = varlink_server_attach_event(m->varlink_server, m->event, SD_EVENT_PRIORITY_NORMAL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
+
+        return 0;
+}
+
+static ssize_t read_datagram(int fd, struct ucred *ret_sender, void **ret) {
+        _cleanup_free_ void *buffer = NULL;
+        ssize_t n, m;
+
+        assert(fd >= 0);
+        assert(ret_sender);
+        assert(ret);
+
+        n = next_datagram_size_fd(fd);
+        if (n < 0)
+                return n;
+
+        buffer = malloc(n + 2);
+        if (!buffer)
+                return -ENOMEM;
+
+        if (ret_sender) {
+                union {
+                        struct cmsghdr cmsghdr;
+                        uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
+                } control;
+                bool found_ucred = false;
+                struct cmsghdr *cmsg;
+                struct msghdr mh;
+                struct iovec iov;
+
+                /* Pass one extra byte, as a size check */
+                iov = IOVEC_MAKE(buffer, n + 1);
+
+                mh = (struct msghdr) {
+                        .msg_iov = &iov,
+                        .msg_iovlen = 1,
+                        .msg_control = &control,
+                        .msg_controllen = sizeof(control),
+                };
+
+                m = recvmsg(fd, &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
+                if (m < 0)
+                        return -errno;
+
+                cmsg_close_all(&mh);
+
+                /* Ensure the size matches what we determined before */
+                if (m != n)
+                        return -EMSGSIZE;
+
+                CMSG_FOREACH(cmsg, &mh)
+                        if (cmsg->cmsg_level == SOL_SOCKET &&
+                            cmsg->cmsg_type == SCM_CREDENTIALS &&
+                            cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
+
+                                memcpy(ret_sender, CMSG_DATA(cmsg), sizeof(struct ucred));
+                                found_ucred = true;
+                        }
+
+                if (!found_ucred)
+                        *ret_sender = (struct ucred) {
+                                .pid = 0,
+                                .uid = UID_INVALID,
+                                .gid = GID_INVALID,
+                        };
+        } else {
+                m = recv(fd, buffer, n + 1, MSG_DONTWAIT);
+                if (m < 0)
+                        return -errno;
+
+                /* Ensure the size matches what we determined before */
+                if (m != n)
+                        return -EMSGSIZE;
+        }
+
+        /* For safety reasons: let's always NUL terminate.  */
+        ((char*) buffer)[n] = 0;
+        *ret = TAKE_PTR(buffer);
+
+        return 0;
+}
+
+static int on_notify_socket(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        _cleanup_strv_free_ char **l = NULL;
+        _cleanup_free_ void *datagram = NULL;
+        struct ucred sender;
+        Manager *m = userdata;
+        ssize_t n;
+        Home *h;
+
+        assert(s);
+        assert(m);
+
+        n = read_datagram(fd, &sender, &datagram);
+        if (IN_SET(n, -EAGAIN, -EINTR))
+                return 0;
+        if (n < 0)
+                return log_error_errno(n, "Failed to read notify datagram: %m");
+
+        if (sender.pid <= 0) {
+                log_warning("Received notify datagram without valid sender PID, ignoring.");
+                return 0;
+        }
+
+        h = hashmap_get(m->homes_by_worker_pid, PID_TO_PTR(sender.pid));
+        if (!h) {
+                log_warning("Recieved notify datagram of unknown process, ignoring.");
+                return 0;
+        }
+
+        l = strv_split(datagram, "\n");
+        if (!l)
+                return log_oom();
+
+        home_process_notify(h, l);
+        return 0;
+}
+
+static int manager_listen_notify(Manager *m) {
+        _cleanup_close_ int fd = -1;
+        union sockaddr_union sa = {
+                .un.sun_family = AF_UNIX,
+                .un.sun_path = "/run/systemd/home/notify",
+        };
+        int r;
+
+        assert(m);
+        assert(!m->notify_socket_event_source);
+
+        fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+        if (fd < 0)
+                return log_error_errno(errno, "Failed to create listening socket: %m");
+
+        (void) mkdir_parents(sa.un.sun_path, 0755);
+        (void) sockaddr_un_unlink(&sa.un);
+
+        if (bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
+                return log_error_errno(errno, "Failed to bind to socket: %m");
+
+        r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true);
+        if (r < 0)
+                return r;
+
+        r = sd_event_add_io(m->event, &m->notify_socket_event_source, fd, EPOLLIN, on_notify_socket, m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate event source for notify socket: %m");
+
+        (void) sd_event_source_set_description(m->notify_socket_event_source, "notify-socket");
+
+        /* Make sure we process sd_notify() before SIGCHLD for any worker, so that we always know the error
+         * number of a client before it exits. */
+        r = sd_event_source_set_priority(m->notify_socket_event_source, SD_EVENT_PRIORITY_NORMAL - 5);
+        if (r < 0)
+                return log_error_errno(r, "Failed to alter priority of NOTIFY_SOCKET event source: %m");
+
+        r = sd_event_source_set_io_fd_own(m->notify_socket_event_source, true);
+        if (r < 0)
+                return log_error_errno(r, "Failed to pass ownership of notify socket: %m");
+
+        return TAKE_FD(fd);
+}
+
+static int manager_add_device(Manager *m, sd_device *d) {
+        _cleanup_free_ char *user_name = NULL, *realm = NULL, *node = NULL;
+        const char *tabletype, *parttype, *partname, *partuuid, *sysfs;
+        sd_id128_t id;
+        int r;
+
+        assert(m);
+        assert(d);
+
+        r = sd_device_get_syspath(d, &sysfs);
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire sysfs path of device: %m");
+
+        r = sd_device_get_property_value(d, "ID_PART_TABLE_TYPE", &tabletype);
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire ID_PART_TABLE_TYPE device property, ignoring: %m");
+
+        if (!streq(tabletype, "gpt")) {
+                log_debug("Found partition (%s) on non-GPT table, ignoring.", sysfs);
+                return 0;
+        }
+
+        r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &parttype);
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire ID_PART_ENTRY_TYPE device property, ignoring: %m");
+        r = sd_id128_from_string(parttype, &id);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to parse ID_PART_ENTRY_TYPE field '%s', ignoring: %m", parttype);
+        if (!sd_id128_equal(id, GPT_USER_HOME)) {
+                log_debug("Found partition (%s) we don't care about, ignoring.", sysfs);
+                return 0;
+        }
+
+        r = sd_device_get_property_value(d, "ID_PART_ENTRY_NAME", &partname);
+        if (r < 0)
+                return log_warning_errno(r, "Failed to acquire ID_PART_ENTRY_NAME device property, ignoring: %m");
+
+        r = split_user_name_realm(partname, &user_name, &realm);
+        if (r == -EINVAL)
+                return log_warning_errno(r, "Found partition with correct partition type but a non-parsable partition name '%s', ignoring.", partname);
+        if (r < 0)
+                return log_error_errno(r, "Failed to validate partition name '%s': %m", partname);
+
+        r = sd_device_get_property_value(d, "ID_FS_UUID", &partuuid);
+        if (r < 0)
+                return log_warning_errno(r, "Failed to acquire ID_FS_UUID device property, ignoring: %m");
+
+        r = sd_id128_from_string(partuuid, &id);
+        if (r < 0)
+                return log_warning_errno(r, "Failed to parse ID_FS_UUID field '%s', ignoring: %m", partuuid);
+
+        if (asprintf(&node, "/dev/disk/by-uuid/" SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(id)) < 0)
+                return log_oom();
+
+        return manager_add_home_by_image(m, user_name, realm, node, sysfs, USER_LUKS, UID_INVALID);
+}
+
+static int manager_on_device(sd_device_monitor *monitor, sd_device *d, void *userdata) {
+        Manager *m = userdata;
+        int r;
+
+        assert(m);
+        assert(d);
+
+        if (device_for_action(d, DEVICE_ACTION_REMOVE)) {
+                const char *sysfs;
+                Home *h;
+
+                r = sd_device_get_syspath(d, &sysfs);
+                if (r < 0) {
+                        log_warning_errno(r, "Failed to acquire sysfs path from device: %m");
+                        return 0;
+                }
+
+                log_info("block device %s has been removed.", sysfs);
+
+                /* Let's see if we previously synthesized a home record from this device, if so, let's just
+                 * revalidate that. Otherwise let's revalidate them all, but asynchronously. */
+                h = hashmap_get(m->homes_by_sysfs, sysfs);
+                if (h)
+                        manager_revalidate_image(m, h);
+                else
+                        manager_enqueue_gc(m, NULL);
+        } else
+                (void) manager_add_device(m, d);
+
+        (void) bus_manager_emit_auto_login_changed(m);
+        return 0;
+}
+
+static int manager_watch_devices(Manager *m) {
+        int r;
+
+        assert(m);
+        assert(!m->device_monitor);
+
+        r = sd_device_monitor_new(&m->device_monitor);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate device monitor: %m");
+
+        r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_monitor, "block", NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to configure device monitor match: %m");
+
+        r = sd_device_monitor_attach_event(m->device_monitor, m->event);
+        if (r < 0)
+                return log_error_errno(r, "Failed to attach device monitor to event loop: %m");
+
+        r = sd_device_monitor_start(m->device_monitor, manager_on_device, m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to start device monitor: %m");
+
+        return 0;
+}
+
+static int manager_enumerate_devices(Manager *m) {
+        _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+        sd_device *d;
+        int r;
+
+        assert(m);
+
+        r = sd_device_enumerator_new(&e);
+        if (r < 0)
+                return r;
+
+        r = sd_device_enumerator_add_match_subsystem(e, "block", true);
+        if (r < 0)
+                return r;
+
+        FOREACH_DEVICE(e, d)
+                (void) manager_add_device(m, d);
+
+        return 0;
+}
+
+static int manager_load_key_pair(Manager *m) {
+        _cleanup_(fclosep) FILE *f = NULL;
+        struct stat st;
+        int r;
+
+        assert(m);
+
+        if (m->private_key) {
+                EVP_PKEY_free(m->private_key);
+                m->private_key = NULL;
+        }
+
+        r = search_and_fopen_nulstr("local.private", "re", NULL, KEY_PATHS_NULSTR, &f);
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
+                return log_error_errno(r, "Failed to read private key file: %m");
+
+        if (fstat(fileno(f), &st) < 0)
+                return log_error_errno(errno, "Failed to stat private key file: %m");
+
+        r = stat_verify_regular(&st);
+        if (r < 0)
+                return log_error_errno(r, "Private key file is not regular: %m");
+
+        if (st.st_uid != 0 || (st.st_mode & 0077) != 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Private key file is readable by more than the root user");
+
+        m->private_key = PEM_read_PrivateKey(f, NULL, NULL, NULL);
+        if (!m->private_key)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to load private key pair");
+
+        log_info("Successfully loaded private key pair.");
+
+        return 1;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(EVP_PKEY_CTX*, EVP_PKEY_CTX_free);
+
+static int manager_generate_key_pair(Manager *m) {
+        _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = NULL;
+        _cleanup_(unlink_and_freep) char *temp_public = NULL, *temp_private = NULL;
+        _cleanup_fclose_ FILE *fpublic = NULL, *fprivate = NULL;
+        int r;
+
+        if (m->private_key) {
+                EVP_PKEY_free(m->private_key);
+                m->private_key = NULL;
+        }
+
+        ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, NULL);
+        if (!ctx)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to allocate Ed25519 key generation context.");
+
+        if (EVP_PKEY_keygen_init(ctx) <= 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize Ed25519 key generation context.");
+
+        log_info("Generating key pair for signing local user identity records.");
+
+        if (EVP_PKEY_keygen(ctx, &m->private_key) <= 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to generate Ed25519 key pair");
+
+        log_info("Successfully created Ed25519 key pair.");
+
+        (void) mkdir_p("/var/lib/systemd/home", 0755);
+
+        /* Write out public key (note that we only do that as a help to the user, we don't make use of this ever */
+        r = fopen_temporary("/var/lib/systemd/home/local.public", &fpublic, &temp_public);
+        if (r < 0)
+                return log_error_errno(errno, "Failed ot open key file for writing: %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);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write private key: %m");
+
+        fpublic = safe_fclose(fpublic);
+
+        /* Write out the private key (this actually writes out both private and public, OpenSSL is confusing) */
+        r = fopen_temporary("/var/lib/systemd/home/local.private", &fprivate, &temp_private);
+        if (r < 0)
+                return log_error_errno(errno, "Failed ot open key file for writing: %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);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write private key: %m");
+
+        fprivate = safe_fclose(fprivate);
+
+        /* Both are written now, move them into place */
+
+        if (rename(temp_public, "/var/lib/systemd/home/local.public") < 0)
+                return log_error_errno(errno, "Failed to move public key file into place: %m");
+        temp_public = mfree(temp_public);
+
+        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");
+        }
+        temp_private = mfree(temp_private);
+
+        return 1;
+}
+
+int manager_acquire_key_pair(Manager *m) {
+        int r;
+
+        assert(m);
+
+        /* Already there? */
+        if (m->private_key)
+                return 1;
+
+        /* First try to load key off disk */
+        r = manager_load_key_pair(m);
+        if (r != 0)
+                return r;
+
+        /* Didn't work, generate a new one */
+        return manager_generate_key_pair(m);
+}
+
+int manager_sign_user_record(Manager *m, UserRecord *u, UserRecord **ret, sd_bus_error *error) {
+        int r;
+
+        assert(m);
+        assert(u);
+        assert(ret);
+
+        r = manager_acquire_key_pair(m);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_KEY, "Can't sign without local key.");
+
+        return user_record_sign(u, m->private_key, ret);
+}
+
+DEFINE_PRIVATE_HASH_OPS_FULL(public_key_hash_ops, char, string_hash_func, string_compare_func, free, EVP_PKEY, EVP_PKEY_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(EVP_PKEY*, EVP_PKEY_free);
+
+static int manager_load_public_key_one(Manager *m, const char *path) {
+        _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_free_ char *fn = NULL;
+        struct stat st;
+        int r;
+
+        assert(m);
+
+        if (streq(basename(path), "local.public")) /* we already loaded the private key, which includes the public one */
+                return 0;
+
+        f = fopen(path, "re");
+        if (!f) {
+                if (errno == ENOENT)
+                        return 0;
+
+                return log_error_errno(errno, "Failed to open public key %s: %m", path);
+        }
+
+        if (fstat(fileno(f), &st) < 0)
+                return log_error_errno(errno, "Failed to stat public key %s: %m", path);
+
+        r = stat_verify_regular(&st);
+        if (r < 0)
+                return log_error_errno(r, "Public key file %s is not a regular file: %m", path);
+
+        if (st.st_uid != 0 || (st.st_mode & 0022) != 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Public key file %s is writable by more than the root user, refusing.", path);
+
+        r = hashmap_ensure_allocated(&m->public_keys, &public_key_hash_ops);
+        if (r < 0)
+                return log_oom();
+
+        pkey = PEM_read_PUBKEY(f, &pkey, NULL, NULL);
+        if (!pkey)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse public key file %s.", path);
+
+        fn = strdup(basename(path));
+        if (!fn)
+                return log_oom();
+
+        r = hashmap_put(m->public_keys, fn, pkey);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add public key to set: %m");
+
+        TAKE_PTR(fn);
+        TAKE_PTR(pkey);
+
+        return 0;
+}
+
+static int manager_load_public_keys(Manager *m) {
+        _cleanup_strv_free_ char **files = NULL;
+        char **i;
+        int r;
+
+        assert(m);
+
+        m->public_keys = hashmap_free(m->public_keys);
+
+        r = conf_files_list_nulstr(
+                        &files,
+                        ".public",
+                        NULL,
+                        CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED,
+                        KEY_PATHS_NULSTR);
+        if (r < 0)
+                return log_error_errno(r, "Failed to assemble list of public key directories: %m");
+
+        STRV_FOREACH(i, files)
+                (void) manager_load_public_key_one(m, *i);
+
+        return 0;
+}
+
+int manager_startup(Manager *m) {
+        int r;
+
+        assert(m);
+
+        r = manager_listen_notify(m);
+        if (r < 0)
+                return r;
+
+        r = manager_connect_bus(m);
+        if (r < 0)
+                return r;
+
+        r = manager_bind_varlink(m);
+        if (r < 0)
+                return r;
+
+        r = manager_load_key_pair(m); /* only try to load it, don't generate any */
+        if (r < 0)
+                return r;
+
+        r = manager_load_public_keys(m);
+        if (r < 0)
+                return r;
+
+        manager_watch_home(m);
+        (void) manager_watch_devices(m);
+
+        (void) manager_enumerate_records(m);
+        (void) manager_enumerate_images(m);
+        (void) manager_enumerate_devices(m);
+
+        /* Let's clean up home directories whose devices got removed while we were not running */
+        (void) manager_enqueue_gc(m, NULL);
+
+        return 0;
+}
+
+void manager_revalidate_image(Manager *m, Home *h) {
+        int r;
+
+        assert(m);
+        assert(h);
+
+        /* Frees an automatically discovered image, if it's synthetic and its image disappeared. Unmounts any
+         * image if it's mounted but it's image vanished. */
+
+        if (h->current_operation || !ordered_set_isempty(h->pending_operations))
+                return;
+
+        if (h->state == HOME_UNFIXATED) {
+                r = user_record_test_image_path(h->record);
+                if (r < 0)
+                        log_warning_errno(r, "Can't determine if image of %s exists, freeing unfixated user: %m", h->user_name);
+                else if (r == USER_TEST_ABSENT)
+                        log_info("Image for %s disappeared, freeing unfixated user.", h->user_name);
+                else
+                        return;
+
+                home_free(h);
+
+        } else if (h->state < 0) {
+
+                r = user_record_test_home_directory(h->record);
+                if (r < 0) {
+                        log_warning_errno(r, "Unable to determine state of home directory, ignoring: %m");
+                        return;
+                }
+
+                if (r == USER_TEST_MOUNTED) {
+                        r = user_record_test_image_path(h->record);
+                        if (r < 0) {
+                                log_warning_errno(r, "Unable to determine state of image path, ignoring: %m");
+                                return;
+                        }
+
+                        if (r == USER_TEST_ABSENT) {
+                                _cleanup_(operation_unrefp) Operation *o = NULL;
+
+                                log_notice("Backing image disappeared while home directory %s was mounted, unmounting it forcibly.", h->user_name);
+                                /* Wowza, the thing is mounted, but the device is gone? Act on it. */
+
+                                r = home_killall(h);
+                                if (r < 0)
+                                        log_warning_errno(r, "Failed to kill processes of user %s, ignoring: %m", h->user_name);
+
+                                /* We enqueue the operation here, after all the home directory might
+                                 * currently already run some operation, and we can deactivate it only after
+                                 * that's complete. */
+                                o = operation_new(OPERATION_DEACTIVATE_FORCE, NULL);
+                                if (!o) {
+                                        log_oom();
+                                        return;
+                                }
+
+                                r = home_schedule_operation(h, o, NULL);
+                                if (r < 0)
+                                        log_warning_errno(r, "Failed to enqueue forced home directory %s deactivation, ignoring: %m", h->user_name);
+                        }
+                }
+        }
+}
+
+int manager_gc_images(Manager *m) {
+        Home *h;
+
+        assert_se(m);
+
+        if (m->gc_focus) {
+                /* Focus on a specific home */
+
+                h = TAKE_PTR(m->gc_focus);
+                manager_revalidate_image(m, h);
+        } else {
+                /* Gc all */
+                Iterator i;
+
+                HASHMAP_FOREACH(h, m->homes_by_name, i)
+                        manager_revalidate_image(m, h);
+        }
+
+        return 0;
+}
+
+static int on_deferred_rescan(sd_event_source *s, void *userdata) {
+        Manager *m = userdata;
+
+        assert(m);
+
+        m->deferred_rescan_event_source = sd_event_source_unref(m->deferred_rescan_event_source);
+
+        manager_enumerate_devices(m);
+        manager_enumerate_images(m);
+        return 0;
+}
+
+int manager_enqueue_rescan(Manager *m) {
+        int r;
+
+        assert(m);
+
+        if (m->deferred_rescan_event_source)
+                return 0;
+
+        if (!m->event)
+                return 0;
+
+        if (IN_SET(sd_event_get_state(m->event), SD_EVENT_FINISHED, SD_EVENT_EXITING))
+                return 0;
+
+        r = sd_event_add_defer(m->event, &m->deferred_rescan_event_source, on_deferred_rescan, m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate rescan event source: %m");
+
+        r = sd_event_source_set_priority(m->deferred_rescan_event_source, SD_EVENT_PRIORITY_IDLE+1);
+        if (r < 0)
+                log_warning_errno(r, "Failed to tweak priority of event source, ignoring: %m");
+
+        (void) sd_event_source_set_description(m->deferred_rescan_event_source, "deferred-rescan");
+        return 1;
+}
+
+static int on_deferred_gc(sd_event_source *s, void *userdata) {
+        Manager *m = userdata;
+
+        assert(m);
+
+        m->deferred_gc_event_source = sd_event_source_unref(m->deferred_gc_event_source);
+
+        manager_gc_images(m);
+        return 0;
+}
+
+int manager_enqueue_gc(Manager *m, Home *focus) {
+        int r;
+
+        assert(m);
+
+        /* This enqueues a request to GC dead homes. It may be called with focus=NULL in which case all homes
+         * will be scanned, or with the parameter set, in which case only that home is checked. */
+
+        if (!m->event)
+                return 0;
+
+        if (IN_SET(sd_event_get_state(m->event), SD_EVENT_FINISHED, SD_EVENT_EXITING))
+                return 0;
+
+        /* If a focus home is specified, then remember to focus just on this home. Otherwise invalidate any
+         * focus that might be set to look at all homes. */
+
+        if (m->deferred_gc_event_source) {
+                if (m->gc_focus != focus) /* not the same focus, then look at everything */
+                        m->gc_focus = NULL;
+
+                return 0;
+        } else
+                m->gc_focus = focus; /* start focussed */
+
+        r = sd_event_add_defer(m->event, &m->deferred_gc_event_source, on_deferred_gc, m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate gc event source: %m");
+
+        r = sd_event_source_set_priority(m->deferred_gc_event_source, SD_EVENT_PRIORITY_IDLE);
+        if (r < 0)
+                log_warning_errno(r, "Failed to tweak priority of event source, ignoring: %m");
+
+        (void) sd_event_source_set_description(m->deferred_gc_event_source, "deferred-gc");
+        return 1;
+}
diff --git a/src/home/homed-manager.h b/src/home/homed-manager.h
new file mode 100644 (file)
index 0000000..00298a3
--- /dev/null
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <openssl/evp.h>
+
+#include "sd-bus.h"
+#include "sd-device.h"
+#include "sd-event.h"
+
+typedef struct Manager Manager;
+
+#include "hashmap.h"
+#include "homed-home.h"
+#include "varlink.h"
+
+#define HOME_UID_MIN 60001
+#define HOME_UID_MAX 60513
+
+struct Manager {
+        sd_event *event;
+        sd_bus *bus;
+
+        Hashmap *polkit_registry;
+
+        Hashmap *homes_by_uid;
+        Hashmap *homes_by_name;
+        Hashmap *homes_by_worker_pid;
+        Hashmap *homes_by_sysfs;
+
+        bool scan_slash_home;
+
+        sd_event_source *inotify_event_source;
+
+        /* An even source we receieve sd_notify() messages from our worker from */
+        sd_event_source *notify_socket_event_source;
+
+        sd_device_monitor *device_monitor;
+
+        sd_event_source *deferred_rescan_event_source;
+        sd_event_source *deferred_gc_event_source;
+        sd_event_source *deferred_auto_login_event_source;
+
+        Home *gc_focus;
+
+        VarlinkServer *varlink_server;
+
+        EVP_PKEY *private_key; /* actually a pair of private and public key */
+        Hashmap *public_keys; /* key name [char*] → publick key [EVP_PKEY*] */
+};
+
+int manager_new(Manager **ret);
+Manager* manager_free(Manager *m);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
+
+int manager_startup(Manager *m);
+
+int manager_augment_record_with_uid(Manager *m, UserRecord *hr);
+
+int manager_enqueue_rescan(Manager *m);
+int manager_enqueue_gc(Manager *m, Home *focus);
+
+int manager_verify_user_record(Manager *m, UserRecord *hr);
+
+int manager_acquire_key_pair(Manager *m);
+int manager_sign_user_record(Manager *m, UserRecord *u, UserRecord **ret, sd_bus_error *error);
+
+int bus_manager_emit_auto_login_changed(Manager *m);
diff --git a/src/home/homed-operation.c b/src/home/homed-operation.c
new file mode 100644 (file)
index 0000000..80dc555
--- /dev/null
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "fd-util.h"
+#include "homed-operation.h"
+
+Operation *operation_new(OperationType type, sd_bus_message *m) {
+        Operation *o;
+
+        assert(type >= 0);
+        assert(type < _OPERATION_MAX);
+
+        o = new(Operation, 1);
+        if (!o)
+                return NULL;
+
+        *o = (Operation) {
+                .type = type,
+                .n_ref = 1,
+                .message = sd_bus_message_ref(m),
+                .send_fd = -1,
+                .result = -1,
+        };
+
+        return o;
+}
+
+static Operation *operation_free(Operation *o) {
+        int r;
+
+        if (!o)
+                return NULL;
+
+        if (o->message && o->result >= 0) {
+
+                if (o->result) {
+                        /* Propagate success */
+                        if (o->send_fd < 0)
+                                r = sd_bus_reply_method_return(o->message, NULL);
+                        else
+                                r = sd_bus_reply_method_return(o->message, "h", o->send_fd);
+
+                } else {
+                        /* Propagate failure */
+                        if (sd_bus_error_is_set(&o->error))
+                                r = sd_bus_reply_method_error(o->message, &o->error);
+                        else
+                                r = sd_bus_reply_method_errnof(o->message, o->ret, "Failed to execute operation: %m");
+                }
+                if (r < 0)
+                        log_warning_errno(r, "Failed ot reply to %s method call, ignoring: %m", sd_bus_message_get_member(o->message));
+        }
+
+        sd_bus_message_unref(o->message);
+        user_record_unref(o->secret);
+        safe_close(o->send_fd);
+        sd_bus_error_free(&o->error);
+
+        return mfree(o);
+}
+
+DEFINE_TRIVIAL_REF_UNREF_FUNC(Operation, operation, operation_free);
+
+void operation_result(Operation *o, int ret, const sd_bus_error *error) {
+        assert(o);
+
+        if (ret >= 0)
+                o->result = true;
+        else {
+                o->ret = ret;
+
+                sd_bus_error_free(&o->error);
+                sd_bus_error_copy(&o->error, error);
+
+                o->result = false;
+        }
+}
diff --git a/src/home/homed-operation.h b/src/home/homed-operation.h
new file mode 100644 (file)
index 0000000..224de91
--- /dev/null
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <sd-bus.h>
+
+#include "user-record.h"
+
+typedef enum OperationType {
+        OPERATION_ACQUIRE,           /* enqueued on AcquireHome() */
+        OPERATION_RELEASE,           /* enqueued on ReleaseHome() */
+        OPERATION_LOCK_ALL,          /* enqueued on LockAllHomes() */
+        OPERATION_PIPE_EOF,          /* enqueued when we see EOF on the per-home reference pipes */
+        OPERATION_DEACTIVATE_FORCE,  /* enqueued on hard $HOME unplug */
+        OPERATION_IMMEDIATE,         /* this is never enqueued, it's just a marker we immediately started executing an operation without enqueuing anything first. */
+        _OPERATION_MAX,
+        _OPERATION_INVALID = -1,
+} OperationType;
+
+/* Encapsulates an operation on one or more home directories. This has two uses:
+ *
+ *     1) For queuing an operation when we need to execute one for some reason but there's already one being
+ *        executed.
+ *
+ *     2) When executing an operation without enqueuing it first (OPERATION_IMMEDIATE)
+ *
+ * Note that a single operation object can encapsulate operations on multiple home directories. This is used
+ * for the LockAllHomes() operation, which is one operation but applies to all homes at once. In case the
+ * operation applies to multiple homes the reference counter is increased once for each, and thus the
+ * operation is fully completed only after it reached zero again.
+ *
+ * The object (optionally) contains a reference of the D-Bus message triggering the operation, which is
+ * replied to when the operation is fully completed, i.e. when n_ref reaches zero.
+ */
+
+typedef struct Operation {
+        unsigned n_ref;
+        OperationType type;
+        sd_bus_message *message;
+
+        UserRecord *secret;
+        int send_fd;   /* pipe fd for AcquireHome() which is taken already when we start the operation */
+
+        int result;    /* < 0 if not completed yet, == 0 on failure, > 0 on success */
+        sd_bus_error error;
+        int ret;
+} Operation;
+
+Operation *operation_new(OperationType type, sd_bus_message *m);
+Operation *operation_ref(Operation *operation);
+Operation *operation_unref(Operation *operation);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Operation*, operation_unref);
+
+void operation_result(Operation *o, int ret, const sd_bus_error *error);
+
+static inline Operation* operation_result_unref(Operation *o, int ret, const sd_bus_error *error) {
+        if (!o)
+                return NULL;
+
+        operation_result(o, ret, error);
+        return operation_unref(o);
+}
diff --git a/src/home/homed-varlink.c b/src/home/homed-varlink.c
new file mode 100644 (file)
index 0000000..c5bbba6
--- /dev/null
@@ -0,0 +1,370 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "group-record.h"
+#include "homed-varlink.h"
+#include "strv.h"
+#include "user-record-util.h"
+#include "user-record.h"
+#include "user-util.h"
+#include "format-util.h"
+
+typedef struct LookupParameters {
+        const char *user_name;
+        const char *group_name;
+        union {
+                uid_t uid;
+                gid_t gid;
+        };
+        const char *service;
+} LookupParameters;
+
+static bool client_is_trusted(Varlink *link, Home *h) {
+        uid_t peer_uid;
+        int r;
+
+        assert(link);
+        assert(h);
+
+        r = varlink_get_peer_uid(link, &peer_uid);
+        if (r < 0) {
+                log_debug_errno(r, "Unable to query peer UID, ignoring: %m");
+                return false;
+        }
+
+        return peer_uid == 0 || peer_uid == h->uid;
+}
+
+static int build_user_json(Home *h, bool trusted, JsonVariant **ret) {
+        _cleanup_(user_record_unrefp) UserRecord *augmented = NULL;
+        UserRecordLoadFlags flags;
+        int r;
+
+        assert(h);
+        assert(ret);
+
+        flags = USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_BINDING|USER_RECORD_STRIP_SECRET|USER_RECORD_ALLOW_STATUS|USER_RECORD_ALLOW_SIGNATURE;
+        if (trusted)
+                flags |= USER_RECORD_ALLOW_PRIVILEGED;
+        else
+                flags |= USER_RECORD_STRIP_PRIVILEGED;
+
+        r = home_augment_status(h, flags, &augmented);
+        if (r < 0)
+                return r;
+
+        return json_build(ret, JSON_BUILD_OBJECT(
+                                          JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(augmented->json)),
+                                          JSON_BUILD_PAIR("incomplete", JSON_BUILD_BOOLEAN(augmented->incomplete))));
+}
+
+static bool home_user_match_lookup_parameters(LookupParameters *p, Home *h) {
+        assert(p);
+        assert(h);
+
+        if (p->user_name && !streq(p->user_name, h->user_name))
+                return false;
+
+        if (uid_is_valid(p->uid) && h->uid != p->uid)
+                return false;
+
+        return true;
+}
+
+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,
+        };
+        Manager *m = userdata;
+        bool trusted;
+        Home *h;
+        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.Home"))
+                return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+        if (uid_is_valid(p.uid))
+                h = hashmap_get(m->homes_by_uid, UID_TO_PTR(p.uid));
+        else if (p.user_name)
+                h = hashmap_get(m->homes_by_name, p.user_name);
+        else {
+                Iterator i;
+
+                /* If neither UID nor name was specified, then dump all homes. Do so with varlink_notify()
+                 * for all entries but the last, so that clients can stream the results, and easily process
+                 * them piecemeal. */
+
+                HASHMAP_FOREACH(h, m->homes_by_name, i) {
+
+                        if (!home_user_match_lookup_parameters(&p, h))
+                                continue;
+
+                        if (v) {
+                                /* An entry set from the previous iteration? Then send it now */
+                                r = varlink_notify(link, v);
+                                if (r < 0)
+                                        return r;
+
+                                v = json_variant_unref(v);
+                        }
+
+                        trusted = client_is_trusted(link, h);
+
+                        r = build_user_json(h, trusted, &v);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (!v)
+                        return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+                return varlink_reply(link, v);
+        }
+
+        if (!h)
+                return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+        if (!home_user_match_lookup_parameters(&p, h))
+                return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
+
+        trusted = client_is_trusted(link, h);
+
+        r = build_user_json(h, trusted, &v);
+        if (r < 0)
+                return r;
+
+        return varlink_reply(link, v);
+}
+
+static int build_group_json(Home *h, JsonVariant **ret) {
+        _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
+        int r;
+
+        assert(h);
+        assert(ret);
+
+        g = group_record_new();
+        if (!g)
+                return -ENOMEM;
+
+        r = group_record_synthesize(g, h->record);
+        if (r < 0)
+                return r;
+
+        assert(!FLAGS_SET(g->mask, USER_RECORD_SECRET));
+        assert(!FLAGS_SET(g->mask, USER_RECORD_PRIVILEGED));
+
+        return json_build(ret,
+                          JSON_BUILD_OBJECT(
+                                          JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(g->json))));
+}
+
+static bool home_group_match_lookup_parameters(LookupParameters *p, Home *h) {
+        assert(p);
+        assert(h);
+
+        if (p->group_name && !streq(h->user_name, p->group_name))
+                return false;
+
+        if (gid_is_valid(p->gid) && h->uid != (uid_t) p->gid)
+                return false;
+
+        return true;
+}
+
+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,
+        };
+        Manager *m = userdata;
+        Home *h;
+        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.Home"))
+                return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+        if (gid_is_valid(p.gid))
+                h = hashmap_get(m->homes_by_uid, UID_TO_PTR((uid_t) p.gid));
+        else if (p.group_name)
+                h = hashmap_get(m->homes_by_name, p.group_name);
+        else {
+                Iterator i;
+
+                HASHMAP_FOREACH(h, m->homes_by_name, i) {
+
+                        if (!home_group_match_lookup_parameters(&p, h))
+                                continue;
+
+                        if (v) {
+                                r = varlink_notify(link, v);
+                                if (r < 0)
+                                        return r;
+
+                                v = json_variant_unref(v);
+                        }
+
+                        r = build_group_json(h, &v);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (!v)
+                        return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+                return varlink_reply(link, v);
+        }
+
+        if (!h)
+                return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+        if (!home_group_match_lookup_parameters(&p, h))
+                return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
+
+        r = build_group_json(h, &v);
+        if (r < 0)
+                return r;
+
+        return varlink_reply(link, v);
+}
+
+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         },
+                {}
+        };
+
+        Manager *m = userdata;
+        LookupParameters p = {};
+        Home *h;
+        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.Home"))
+                return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+        if (p.user_name) {
+                const char *last = NULL;
+                char **i;
+
+                h = hashmap_get(m->homes_by_name, p.user_name);
+                if (!h)
+                        return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+                if (p.group_name) {
+                        if (!strv_contains(h->record->member_of, p.group_name))
+                                return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+                        return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h->user_name)),
+                                                                      JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p.group_name))));
+                }
+
+                STRV_FOREACH(i, h->record->member_of) {
+                        if (last) {
+                                r = varlink_notifyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h->user_name)),
+                                                                            JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last))));
+                                if (r < 0)
+                                        return r;
+                        }
+
+                        last = *i;
+                }
+
+                if (last)
+                        return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h->user_name)),
+                                                                      JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last))));
+
+        } else if (p.group_name) {
+                const char *last = NULL;
+                Iterator i;
+
+                HASHMAP_FOREACH(h, m->homes_by_name, i) {
+
+                        if (!strv_contains(h->record->member_of, p.group_name))
+                                continue;
+
+                        if (last) {
+                                r = varlink_notifyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last)),
+                                                                            JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p.group_name))));
+                                if (r < 0)
+                                        return r;
+                        }
+
+                        last = h->user_name;
+                }
+
+                if (last)
+                        return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last)),
+                                                                      JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p.group_name))));
+        } else {
+                const char *last_user_name = NULL, *last_group_name = NULL;
+                Iterator i;
+
+                HASHMAP_FOREACH(h, m->homes_by_name, i) {
+                        char **j;
+
+                        STRV_FOREACH(j, h->record->member_of) {
+
+                                if (last_user_name) {
+                                        assert(last_group_name);
+
+                                        r = varlink_notifyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
+                                                                                    JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
+
+                                        if (r < 0)
+                                                return r;
+                                }
+
+                                last_user_name = h->user_name;
+                                last_group_name = *j;
+                        }
+                }
+
+                if (last_user_name) {
+                        assert(last_group_name);
+                        return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
+                                                                      JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
+                }
+        }
+
+        return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+}
diff --git a/src/home/homed-varlink.h b/src/home/homed-varlink.h
new file mode 100644 (file)
index 0000000..4454d23
--- /dev/null
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "homed-manager.h"
+
+int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata);
+int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata);
+int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata);
diff --git a/src/home/homed.c b/src/home/homed.c
new file mode 100644 (file)
index 0000000..ca43558
--- /dev/null
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "daemon-util.h"
+#include "homed-manager.h"
+#include "log.h"
+#include "main-func.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;
+        int r;
+
+        log_setup_service();
+
+        umask(0022);
+
+        if (argc != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
+
+        if (setenv("SYSTEMD_BYPASS_USERDB", "io.systemd.Home", 1) < 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to set $SYSTEMD_BYPASS_USERDB: %m");
+
+        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, -1) >= 0);
+
+        r = manager_new(&m);
+        if (r < 0)
+                return log_error_errno(r, "Could not create manager: %m");
+
+        r = manager_startup(m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to start up daemon: %m");
+
+        notify_stop = notify_start(NOTIFY_READY, NOTIFY_STOPPING);
+
+        r = sd_event_loop(m->event);
+        if (r < 0)
+                return log_error_errno(r, "Event loop failed: %m");
+
+        return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/home/homework-cifs.c b/src/home/homework-cifs.c
new file mode 100644 (file)
index 0000000..27f2981
--- /dev/null
@@ -0,0 +1,214 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "format-util.h"
+#include "fs-util.h"
+#include "homework-cifs.h"
+#include "homework-mount.h"
+#include "mount-util.h"
+#include "process-util.h"
+#include "strv.h"
+#include "tmpfile-util.h"
+
+int home_prepare_cifs(
+                UserRecord *h,
+                bool already_activated,
+                HomeSetup *setup) {
+
+        assert(h);
+        assert(setup);
+        assert(user_record_storage(h) == USER_CIFS);
+
+        if (already_activated)
+                setup->root_fd = open(user_record_home_directory(h), O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+        else {
+                bool mounted = false;
+                char **pw;
+                int r;
+
+                r = home_unshare_and_mount(NULL, NULL, false);
+                if (r < 0)
+                        return r;
+
+                STRV_FOREACH(pw, h->password) {
+                        _cleanup_(unlink_and_freep) char *p = NULL;
+                        _cleanup_free_ char *options = NULL;
+                        _cleanup_(fclosep) FILE *f = NULL;
+                        pid_t mount_pid;
+                        int exit_status;
+
+                        r = fopen_temporary(NULL, &f, &p);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to create temporary credentials file: %m");
+
+                        fprintf(f,
+                                "username=%s\n"
+                                "password=%s\n",
+                                user_record_cifs_user_name(h),
+                                *pw);
+
+                        if (h->cifs_domain)
+                                fprintf(f, "domain=%s\n", h->cifs_domain);
+
+                        r = fflush_and_check(f);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to write temporary credentials file: %m");
+
+                        f = safe_fclose(f);
+
+                        if (asprintf(&options, "credentials=%s,uid=" UID_FMT ",forceuid,gid=" UID_FMT ",forcegid,file_mode=0%3o,dir_mode=0%3o",
+                                     p, h->uid, h->uid, h->access_mode, h->access_mode) < 0)
+                                return log_oom();
+
+                        r = safe_fork("(mount)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_STDOUT_TO_STDERR, &mount_pid);
+                        if (r < 0)
+                                return r;
+                        if (r == 0) {
+                                /* Child */
+                                execl("/bin/mount", "/bin/mount", "-n", "-t", "cifs",
+                                      h->cifs_service, "/run/systemd/user-home-mount",
+                                      "-o", options, NULL);
+
+                                log_error_errno(errno, "Failed to execute fsck: %m");
+                                _exit(EXIT_FAILURE);
+                        }
+
+                        exit_status = wait_for_terminate_and_check("mount", mount_pid, WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS);
+                        if (exit_status < 0)
+                                return exit_status;
+                        if (exit_status != EXIT_SUCCESS)
+                                return -EPROTO;
+
+                        mounted = true;
+                        break;
+                }
+
+                if (!mounted)
+                        return log_error_errno(ENOKEY, "Failed to mount home directory with supplied password.");
+
+                setup->root_fd = open("/run/systemd/user-home-mount", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+        }
+        if (setup->root_fd < 0)
+                return log_error_errno(errno, "Failed to open home directory: %m");
+
+        return 0;
+}
+
+int home_activate_cifs(
+                UserRecord *h,
+                char ***pkcs11_decrypted_passwords,
+                UserRecord **ret_home) {
+
+        _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+        _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
+        const char *hdo, *hd;
+        int r;
+
+        assert(h);
+        assert(user_record_storage(h) == USER_CIFS);
+        assert(ret_home);
+
+        if (!h->cifs_service)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks CIFS service, refusing.");
+
+        assert_se(hdo = user_record_home_directory(h));
+        hd = strdupa(hdo); /* copy the string out, since it might change later in the home record object */
+
+        r = home_prepare_cifs(h, false, &setup);
+        if (r < 0)
+                return r;
+
+        r = home_refresh(h, &setup, NULL, pkcs11_decrypted_passwords, NULL, &new_home);
+        if (r < 0)
+                return r;
+
+        setup.root_fd = safe_close(setup.root_fd);
+
+        r = home_move_mount(NULL, hd);
+        if (r < 0)
+                return r;
+
+        setup.undo_mount = false;
+
+        log_info("Everything completed.");
+
+        *ret_home = TAKE_PTR(new_home);
+        return 1;
+}
+
+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;
+
+        assert(h);
+        assert(user_record_storage(h) == USER_CIFS);
+        assert(ret_home);
+
+        if (!h->cifs_service)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks CIFS service, refusing.");
+
+        if (access("/sbin/mount.cifs", F_OK) < 0) {
+                if (errno == ENOENT)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOLINK), "/sbin/mount.cifs is missing.");
+
+                return log_error_errno(errno, "Unable to detect whether /sbin/mount.cifs exists: %m");
+        }
+
+        r = home_prepare_cifs(h, false, &setup);
+        if (r < 0)
+                return r;
+
+        copy = fcntl(setup.root_fd, F_DUPFD_CLOEXEC, 3);
+        if (copy < 0)
+                return -errno;
+
+        d = fdopendir(copy);
+        if (!d) {
+                safe_close(copy);
+                return -errno;
+        }
+
+        errno = 0;
+        if (readdir_no_dot(d))
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTEMPTY), "Selected CIFS directory not empty, refusing.");
+        if (errno != 0)
+                return log_error_errno(errno, "Failed to detect if CIFS directory is empty: %m");
+
+        r = home_populate(h, setup.root_fd);
+        if (r < 0)
+                return r;
+
+        r = home_sync_and_statfs(setup.root_fd, NULL);
+        if (r < 0)
+                return r;
+
+        r = user_record_clone(h, USER_RECORD_LOAD_MASK_SECRET, &new_home);
+        if (r < 0)
+                return log_error_errno(r, "Failed to clone record: %m");
+
+        r = user_record_add_binding(
+                        new_home,
+                        USER_CIFS,
+                        NULL,
+                        SD_ID128_NULL,
+                        SD_ID128_NULL,
+                        SD_ID128_NULL,
+                        NULL,
+                        NULL,
+                        UINT64_MAX,
+                        NULL,
+                        NULL,
+                        h->uid,
+                        (gid_t) h->uid);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add binding to record: %m");
+
+        log_info("Everything completed.");
+
+        *ret_home = TAKE_PTR(new_home);
+        return 0;
+}
diff --git a/src/home/homework-cifs.h b/src/home/homework-cifs.h
new file mode 100644 (file)
index 0000000..346be88
--- /dev/null
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "homework.h"
+#include "user-record.h"
+
+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_create_cifs(UserRecord *h, UserRecord **ret_home);
diff --git a/src/home/homework-directory.c b/src/home/homework-directory.c
new file mode 100644 (file)
index 0000000..8a4cb17
--- /dev/null
@@ -0,0 +1,242 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/mount.h>
+
+#include "btrfs-util.h"
+#include "fd-util.h"
+#include "homework-directory.h"
+#include "homework-quota.h"
+#include "mkdir.h"
+#include "mount-util.h"
+#include "path-util.h"
+#include "rm-rf.h"
+#include "tmpfile-util.h"
+#include "umask-util.h"
+
+int home_prepare_directory(UserRecord *h, bool already_activated, HomeSetup *setup) {
+        assert(h);
+        assert(setup);
+
+        setup->root_fd = open(user_record_image_path(h), O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+        if (setup->root_fd < 0)
+                return log_error_errno(errno, "Failed to open home directory: %m");
+
+        return 0;
+}
+
+int home_activate_directory(
+                UserRecord *h,
+                char ***pkcs11_decrypted_passwords,
+                UserRecord **ret_home) {
+
+        _cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *header_home = NULL;
+        _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+        const char *hdo, *hd, *ipo, *ip;
+        int r;
+
+        assert(h);
+        assert(IN_SET(user_record_storage(h), USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT));
+        assert(ret_home);
+
+        assert_se(ipo = user_record_image_path(h));
+        ip = strdupa(ipo); /* copy out, since reconciliation might cause changing of the field */
+
+        assert_se(hdo = user_record_home_directory(h));
+        hd = strdupa(hdo);
+
+        r = home_prepare(h, false, pkcs11_decrypted_passwords, &setup, &header_home);
+        if (r < 0)
+                return r;
+
+        r = home_refresh(h, &setup, header_home, pkcs11_decrypted_passwords, NULL, &new_home);
+        if (r < 0)
+                return r;
+
+        setup.root_fd = safe_close(setup.root_fd);
+
+        /* Create mount point to mount over if necessary */
+        if (!path_equal(ip, hd))
+                (void) mkdir_p(hd, 0700);
+
+        /* Create a mount point (even if the directory is already placed correctly), as a way to indicate
+         * this mount point is now "activated". Moreover, we want to set per-user
+         * MS_NOSUID/MS_NOEXEC/MS_NODEV. */
+        r = mount_verbose(LOG_ERR, ip, hd, NULL, MS_BIND, NULL);
+        if (r < 0)
+                return r;
+
+        r = mount_verbose(LOG_ERR, NULL, hd, NULL, MS_BIND|MS_REMOUNT|user_record_mount_flags(h), NULL);
+        if (r < 0) {
+                (void) umount_verbose(hd);
+                return r;
+        }
+
+        log_info("Everything completed.");
+
+        *ret_home = TAKE_PTR(new_home);
+        return 0;
+}
+
+int home_create_directory_or_subvolume(UserRecord *h, UserRecord **ret_home) {
+        _cleanup_(rm_rf_subvolume_and_freep) char *temporary = NULL;
+        _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
+        _cleanup_close_ int root_fd = -1;
+        _cleanup_free_ char *d = NULL;
+        const char *ip;
+        int r;
+
+        assert(h);
+        assert(IN_SET(user_record_storage(h), USER_DIRECTORY, USER_SUBVOLUME));
+        assert(ret_home);
+
+        assert_se(ip = user_record_image_path(h));
+
+        r = tempfn_random(ip, "homework", &d);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate temporary directory: %m");
+
+        (void) mkdir_parents(d, 0755);
+
+        switch (user_record_storage(h)) {
+
+        case USER_SUBVOLUME:
+                RUN_WITH_UMASK(0077)
+                        r = btrfs_subvol_make(d);
+
+                if (r >= 0) {
+                        log_info("Subvolume created.");
+
+                        if (h->disk_size != UINT64_MAX) {
+
+                                /* Enable quota for the subvolume we just created. Note we don't check for
+                                 * errors here and only log about debug level about this. */
+                                r = btrfs_quota_enable(d, true);
+                                if (r < 0)
+                                        log_debug_errno(r, "Failed to enable quota on %s, ignoring: %m", d);
+
+                                r = btrfs_subvol_auto_qgroup(d, 0, false);
+                                if (r < 0)
+                                        log_debug_errno(r, "Failed to set up automatic quota group on %s, ignoring: %m", d);
+
+                                /* Actually configure the quota. We also ignore errors here, but we do log
+                                 * about them loudly, to keep things discoverable even though we don't
+                                 * consider lacking quota support in kernel fatal. */
+                                (void) home_update_quota_btrfs(h, d);
+                        }
+
+                        break;
+                }
+                if (r != -ENOTTY)
+                        return log_error_errno(r, "Failed to create temporary home directory subvolume %s: %m", d);
+
+                log_info("Creating subvolume %s is not supported, as file system does not support subvolumes. Falling back to regular directory.", d);
+                _fallthrough_;
+
+        case USER_DIRECTORY:
+
+                if (mkdir(d, 0700) < 0)
+                        return log_error_errno(errno, "Failed to create temporary home directory %s: %m", d);
+
+                (void) home_update_quota_classic(h, d);
+                break;
+
+        default:
+                assert_not_reached("unexpected storage");
+        }
+
+        temporary = TAKE_PTR(d); /* Needs to be destroyed now */
+
+        root_fd = open(temporary, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+        if (root_fd < 0)
+                return log_error_errno(errno, "Failed to open temporary home directory: %m");
+
+        r = home_populate(h, root_fd);
+        if (r < 0)
+                return r;
+
+        r = home_sync_and_statfs(root_fd, NULL);
+        if (r < 0)
+                return r;
+
+        r = user_record_clone(h, USER_RECORD_LOAD_MASK_SECRET, &new_home);
+        if (r < 0)
+                return log_error_errno(r, "Failed to clone record: %m");
+
+        r = user_record_add_binding(
+                        new_home,
+                        user_record_storage(h),
+                        ip,
+                        SD_ID128_NULL,
+                        SD_ID128_NULL,
+                        SD_ID128_NULL,
+                        NULL,
+                        NULL,
+                        UINT64_MAX,
+                        NULL,
+                        NULL,
+                        h->uid,
+                        (gid_t) h->uid);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add binding to record: %m");
+
+        if (rename(temporary, ip) < 0)
+                return log_error_errno(errno, "Failed to rename %s to %s: %m", temporary, ip);
+
+        temporary = mfree(temporary);
+
+        log_info("Everything completed.");
+
+        *ret_home = TAKE_PTR(new_home);
+        return 0;
+}
+
+int home_resize_directory(
+                UserRecord *h,
+                bool already_activated,
+                char ***pkcs11_decrypted_passwords,
+                HomeSetup *setup,
+                UserRecord **ret_home) {
+
+        _cleanup_(user_record_unrefp) UserRecord *embedded_home = NULL, *new_home = NULL;
+        int r;
+
+        assert(h);
+        assert(setup);
+        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);
+        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);
+        if (r < 0)
+                return r;
+
+        r = home_update_quota_auto(h, NULL);
+        if (ERRNO_IS_NOT_SUPPORTED(r))
+                return -ESOCKTNOSUPPORT; /* make recognizable */
+        if (r < 0)
+                return r;
+
+        r = home_store_embedded_identity(new_home, setup->root_fd, h->uid, embedded_home);
+        if (r < 0)
+                return r;
+
+        r = home_extend_embedded_identity(new_home, h, setup);
+        if (r < 0)
+                return r;
+
+        r = home_sync_and_statfs(setup->root_fd, NULL);
+        if (r < 0)
+                return r;
+
+        r = home_setup_undo(setup);
+        if (r < 0)
+                return r;
+
+        log_info("Everything completed.");
+
+        *ret_home = TAKE_PTR(new_home);
+        return 0;
+}
diff --git a/src/home/homework-directory.h b/src/home/homework-directory.h
new file mode 100644 (file)
index 0000000..047c3a7
--- /dev/null
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "homework.h"
+#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_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);
diff --git a/src/home/homework-fscrypt.c b/src/home/homework-fscrypt.c
new file mode 100644 (file)
index 0000000..696e265
--- /dev/null
@@ -0,0 +1,644 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <linux/fs.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <sys/ioctl.h>
+#include <sys/xattr.h>
+
+#include "errno-util.h"
+#include "fd-util.h"
+#include "hexdecoct.h"
+#include "homework-fscrypt.h"
+#include "homework-quota.h"
+#include "memory-util.h"
+#include "missing_keyctl.h"
+#include "missing_syscall.h"
+#include "mkdir.h"
+#include "nulstr-util.h"
+#include "openssl-util.h"
+#include "parse-util.h"
+#include "process-util.h"
+#include "random-util.h"
+#include "rm-rf.h"
+#include "stdio-util.h"
+#include "strv.h"
+#include "tmpfile-util.h"
+#include "user-util.h"
+#include "xattr-util.h"
+
+static int fscrypt_upload_volume_key(
+                const uint8_t key_descriptor[static FS_KEY_DESCRIPTOR_SIZE],
+                const void *volume_key,
+                size_t volume_key_size,
+                key_serial_t where) {
+
+        _cleanup_free_ char *hex = NULL;
+        const char *description;
+        struct fscrypt_key key;
+        key_serial_t serial;
+
+        assert(key_descriptor);
+        assert(volume_key);
+        assert(volume_key_size > 0);
+
+        if (volume_key_size > sizeof(key.raw))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume key too long.");
+
+        hex = hexmem(key_descriptor, FS_KEY_DESCRIPTOR_SIZE);
+        if (!hex)
+                return log_oom();
+
+        description = strjoina("fscrypt:", hex);
+
+        key = (struct fscrypt_key) {
+                .size = volume_key_size,
+        };
+        memcpy(key.raw, volume_key, volume_key_size);
+
+        /* Upload to the kernel */
+        serial = add_key("logon", description, &key, sizeof(key), where);
+        explicit_bzero_safe(&key, sizeof(key));
+
+        if (serial < 0)
+                return log_error_errno(errno, "Failed to install master key in keyring: %m");
+
+        log_info("Uploaded encryption key to kernel.");
+
+        return 0;
+}
+
+static void calculate_key_descriptor(
+                const void *key,
+                size_t key_size,
+                uint8_t ret_key_descriptor[static FS_KEY_DESCRIPTOR_SIZE]) {
+
+        uint8_t hashed[512 / 8] = {}, hashed2[512 / 8] = {};
+
+        /* Derive the key descriptor from the volume key via double SHA512, in order to be compatible with e4crypt */
+
+        assert_se(SHA512(key, key_size, hashed) == hashed);
+        assert_se(SHA512(hashed, sizeof(hashed), hashed2) == hashed2);
+
+        assert_cc(sizeof(hashed2) >= FS_KEY_DESCRIPTOR_SIZE);
+
+        memcpy(ret_key_descriptor, hashed2, FS_KEY_DESCRIPTOR_SIZE);
+}
+
+static int fscrypt_slot_try_one(
+                const char *password,
+                const void *salt, size_t salt_size,
+                const void *encrypted, size_t encrypted_size,
+                const uint8_t match_key_descriptor[static FS_KEY_DESCRIPTOR_SIZE],
+                void **ret_decrypted, size_t *ret_decrypted_size) {
+
+
+        _cleanup_(EVP_CIPHER_CTX_freep) EVP_CIPHER_CTX *context = NULL;
+        _cleanup_(erase_and_freep) void *decrypted = NULL;
+        uint8_t key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
+        int decrypted_size_out1, decrypted_size_out2;
+        uint8_t derived[512 / 8] = {};
+        size_t decrypted_size;
+        const EVP_CIPHER *cc;
+        int r;
+
+        assert(password);
+        assert(salt);
+        assert(salt_size > 0);
+        assert(encrypted);
+        assert(encrypted_size > 0);
+        assert(match_key_descriptor);
+
+        /* Our construction is like this:
+         *
+         *   1. In each key slot we store a salt value plus the encrypted volume key
+         *
+         *   2. Unlocking is via calculating PBKDF2-HMAC-SHA512 of the supplied password (in combination with
+         *      the salt), then using the first 256 bit of the hash as key for decrypting the encrypted
+         *      volume key in AES256 counter mode.
+         *
+         *   3. Writing a password is similar: calculate PBKDF2-HMAC-SHA512 of the supplied password (in
+         *      combination with the salt), then encrypt the volume key in AES256 counter mode with the
+         *      resulting hash.
+         */
+
+        if (PKCS5_PBKDF2_HMAC(
+                            password, strlen(password),
+                            salt, salt_size,
+                            0xFFFF, EVP_sha512(),
+                            sizeof(derived), derived) != 1) {
+                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PBKDF2 failed");
+                goto finish;
+        }
+
+        context = EVP_CIPHER_CTX_new();
+        if (!context) {
+                r = log_oom();
+                goto finish;
+        }
+
+        /* We use AES256 in counter mode */
+        assert_se(cc = EVP_aes_256_ctr());
+
+        /* We only use the first half of the derived key */
+        assert(sizeof(derived) >= (size_t) EVP_CIPHER_key_length(cc));
+
+        if (EVP_DecryptInit_ex(context, cc, NULL, derived, NULL) != 1)  {
+                r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize decryption context.");
+                goto finish;
+        }
+
+        /* Flush out the derived key now, we don't need it anymore */
+        explicit_bzero_safe(derived, sizeof(derived));
+
+        decrypted_size = encrypted_size + EVP_CIPHER_key_length(cc) * 2;
+        decrypted = malloc(decrypted_size);
+        if (!decrypted)
+                return log_oom();
+
+        if (EVP_DecryptUpdate(context, (uint8_t*) decrypted, &decrypted_size_out1, encrypted, encrypted_size) != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to decrypt volume key.");
+
+        assert((size_t) decrypted_size_out1 <= decrypted_size);
+
+        if (EVP_DecryptFinal_ex(context, (uint8_t*) decrypted_size + decrypted_size_out1, &decrypted_size_out2) != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finish decryption of volume key.");
+
+        assert((size_t) decrypted_size_out1 + (size_t) decrypted_size_out2 < decrypted_size);
+        decrypted_size = (size_t) decrypted_size_out1 + (size_t) decrypted_size_out2;
+
+        calculate_key_descriptor(decrypted, decrypted_size, key_descriptor);
+
+        if (memcmp(key_descriptor, match_key_descriptor, FS_KEY_DESCRIPTOR_SIZE) != 0)
+                return -ENOANO; /* don't log here */
+
+        r = fscrypt_upload_volume_key(key_descriptor, decrypted, decrypted_size, KEY_SPEC_THREAD_KEYRING);
+        if (r < 0)
+                return r;
+
+        if (ret_decrypted)
+                *ret_decrypted = TAKE_PTR(decrypted);
+        if (ret_decrypted_size)
+                *ret_decrypted_size = decrypted_size;
+
+        return 0;
+
+finish:
+        explicit_bzero_safe(derived, sizeof(derived));
+        return r;
+}
+
+static int fscrypt_slot_try_many(
+                char **passwords,
+                const void *salt, size_t salt_size,
+                const void *encrypted, size_t encrypted_size,
+                const uint8_t match_key_descriptor[static FS_KEY_DESCRIPTOR_SIZE],
+                void **ret_decrypted, size_t *ret_decrypted_size) {
+
+        char **i;
+        int r;
+
+        STRV_FOREACH(i, passwords) {
+                r = fscrypt_slot_try_one(*i, salt, salt_size, encrypted, encrypted_size, match_key_descriptor, ret_decrypted, ret_decrypted_size);
+                if (r != -ENOANO)
+                        return r;
+        }
+
+        return -ENOANO;
+}
+
+static int fscrypt_setup(
+                char **pkcs11_decrypted_passwords,
+                char **password,
+                HomeSetup *setup,
+                void **ret_volume_key,
+                size_t *ret_volume_key_size) {
+
+        _cleanup_free_ char *xattr_buf = NULL;
+        const char *xa;
+        int r;
+
+        assert(setup);
+        assert(setup->root_fd >= 0);
+
+        r = flistxattr_malloc(setup->root_fd, &xattr_buf);
+        if (r < 0)
+                return log_error_errno(errno, "Failed to retrieve xattr list: %m");
+
+        NULSTR_FOREACH(xa, xattr_buf) {
+                _cleanup_free_ void *salt = NULL, *encrypted = NULL;
+                _cleanup_free_ char *value = NULL;
+                size_t salt_size, encrypted_size;
+                const char *nr, *e;
+                int n;
+
+                /* Check if this xattr has the format 'trusted.fscrypt_slot<nr>' where '<nr>' is a 32bit unsigned integer */
+                nr = startswith(xa, "trusted.fscrypt_slot");
+                if (!nr)
+                        continue;
+                if (safe_atou32(nr, NULL) < 0)
+                        continue;
+
+                n = fgetxattr_malloc(setup->root_fd, xa, &value);
+                if (n == -ENODATA) /* deleted by now? */
+                        continue;
+                if (n < 0)
+                        return log_error_errno(n, "Failed to read %s xattr: %m", xa);
+
+                e = memchr(value, ':', n);
+                if (!e)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "xattr %s lacks ':' separator: %m", xa);
+
+                r = unbase64mem(value, e - value, &salt, &salt_size);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to decode salt of %s: %m", xa);
+                r = unbase64mem(e+1, n - (e - value) - 1, &encrypted, &encrypted_size);
+                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 = fscrypt_slot_try_many(
+                                        password,
+                                        salt, salt_size,
+                                        encrypted, encrypted_size,
+                                        setup->fscrypt_key_descriptor,
+                                        ret_volume_key, ret_volume_key_size);
+                if (r < 0) {
+                        if (r != -ENOANO)
+                                return r;
+                } else
+                        return 0;
+        }
+
+        return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Failed to set up home directory with provided passwords.");
+}
+
+int home_prepare_fscrypt(
+                UserRecord *h,
+                bool already_activated,
+                char ***pkcs11_decrypted_passwords,
+                HomeSetup *setup) {
+
+        _cleanup_(erase_and_freep) void *volume_key = NULL;
+        struct fscrypt_policy policy = {};
+        size_t volume_key_size = 0;
+        const char *ip;
+        int r;
+
+        assert(h);
+        assert(setup);
+        assert(user_record_storage(h) == USER_FSCRYPT);
+
+        assert_se(ip = user_record_image_path(h));
+
+        setup->root_fd = open(ip, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+        if (setup->root_fd < 0)
+                return log_error_errno(errno, "Failed to open home directory: %m");
+
+        if (ioctl(setup->root_fd, FS_IOC_GET_ENCRYPTION_POLICY, &policy) < 0) {
+                if (errno == ENODATA)
+                        return log_error_errno(errno, "Home directory %s is not encrypted.", ip);
+                if (ERRNO_IS_NOT_SUPPORTED(errno)) {
+                        log_error_errno(errno, "File system does not support fscrypt: %m");
+                        return -ENOLINK; /* make recognizable */
+                }
+                return log_error_errno(errno, "Failed to acquire encryption policy of %s: %m", ip);
+        }
+
+        memcpy(setup->fscrypt_key_descriptor, policy.master_key_descriptor, FS_KEY_DESCRIPTOR_SIZE);
+
+        r = fscrypt_setup(
+                        pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL,
+                        h->password,
+                        setup,
+                        &volume_key,
+                        &volume_key_size);
+        if (r < 0)
+                return r;
+
+        /* Also install the access key in the user's own keyring */
+
+        if (uid_is_valid(h->uid)) {
+                r = safe_fork("(sd-addkey)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed install encryption key in user's keyring: %m");
+                if (r == 0) {
+                        gid_t gid;
+
+                        /* Child */
+
+                        gid = user_record_gid(h);
+                        if (setresgid(gid, gid, gid) < 0) {
+                                log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
+                                _exit(EXIT_FAILURE);
+                        }
+
+                        if (setgroups(0, NULL) < 0) {
+                                log_error_errno(errno, "Failed to reset auxiliary groups list: %m");
+                                _exit(EXIT_FAILURE);
+                        }
+
+                        if (setresuid(h->uid, h->uid, h->uid) < 0) {
+                                log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", h->uid);
+                                _exit(EXIT_FAILURE);
+                        }
+
+                        r = fscrypt_upload_volume_key(
+                                        setup->fscrypt_key_descriptor,
+                                        volume_key,
+                                        volume_key_size,
+                                        KEY_SPEC_USER_KEYRING);
+                        if (r < 0)
+                                _exit(EXIT_FAILURE);
+
+                        _exit(EXIT_SUCCESS);
+                }
+        }
+
+        return 0;
+}
+
+static int fscrypt_slot_set(
+                int root_fd,
+                const void *volume_key,
+                size_t volume_key_size,
+                const char *password,
+                uint32_t nr) {
+
+        _cleanup_free_ char *salt_base64 = NULL, *encrypted_base64 = NULL, *joined = NULL;
+        char label[STRLEN("trusted.fscrypt_slot") + DECIMAL_STR_MAX(nr) + 1];
+        _cleanup_(EVP_CIPHER_CTX_freep) EVP_CIPHER_CTX *context = NULL;
+        int r, encrypted_size_out1, encrypted_size_out2;
+        uint8_t salt[64], derived[512 / 8] = {};
+        _cleanup_free_ void *encrypted = NULL;
+        const EVP_CIPHER *cc;
+        size_t encrypted_size;
+
+        r = genuine_random_bytes(salt, sizeof(salt), RANDOM_BLOCK);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate salt: %m");
+
+        if (PKCS5_PBKDF2_HMAC(
+                            password, strlen(password),
+                            salt, sizeof(salt),
+                            0xFFFF, EVP_sha512(),
+                            sizeof(derived), derived) != 1) {
+                r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PBKDF2 failed");
+                goto finish;
+        }
+
+        context = EVP_CIPHER_CTX_new();
+        if (!context) {
+                r = log_oom();
+                goto finish;
+        }
+
+        /* We use AES256 in counter mode */
+        cc = EVP_aes_256_ctr();
+
+        /* We only use the first half of the derived key */
+        assert(sizeof(derived) >= (size_t) EVP_CIPHER_key_length(cc));
+
+        if (EVP_EncryptInit_ex(context, cc, NULL, derived, NULL) != 1)  {
+                r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize encryption context.");
+                goto finish;
+        }
+
+        /* Flush out the derived key now, we don't need it anymore */
+        explicit_bzero_safe(derived, sizeof(derived));
+
+        encrypted_size = volume_key_size + EVP_CIPHER_key_length(cc) * 2;
+        encrypted = malloc(encrypted_size);
+        if (!encrypted)
+                return log_oom();
+
+        if (EVP_EncryptUpdate(context, (uint8_t*) encrypted, &encrypted_size_out1, volume_key, volume_key_size) != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to encrypt volume key.");
+
+        assert((size_t) encrypted_size_out1 <= encrypted_size);
+
+        if (EVP_EncryptFinal_ex(context, (uint8_t*) encrypted_size + encrypted_size_out1, &encrypted_size_out2) != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finish encryption of volume key.");
+
+        assert((size_t) encrypted_size_out1 + (size_t) encrypted_size_out2 < encrypted_size);
+        encrypted_size = (size_t) encrypted_size_out1 + (size_t) encrypted_size_out2;
+
+        r = base64mem(salt, sizeof(salt), &salt_base64);
+        if (r < 0)
+                return log_oom();
+
+        r = base64mem(encrypted, encrypted_size, &encrypted_base64);
+        if (r < 0)
+                return log_oom();
+
+        joined = strjoin(salt_base64, ":", encrypted_base64);
+        if (!joined)
+                return log_oom();
+
+        xsprintf(label, "trusted.fscrypt_slot%" PRIu32, nr);
+        if (fsetxattr(root_fd, label, joined, strlen(joined), 0) < 0)
+                return log_error_errno(errno, "Failed to write xattr %s: %m", label);
+
+        log_info("Written key slot %s.", label);
+
+        return 0;
+
+finish:
+        explicit_bzero_safe(derived, sizeof(derived));
+        return r;
+}
+
+int home_create_fscrypt(
+                UserRecord *h,
+                char **effective_passwords,
+                UserRecord **ret_home) {
+
+        _cleanup_(rm_rf_physical_and_freep) char *temporary = NULL;
+        _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
+        _cleanup_(erase_and_freep) void *volume_key = NULL;
+        struct fscrypt_policy policy = {};
+        size_t volume_key_size = 512 / 8;
+        _cleanup_close_ int root_fd = -1;
+        _cleanup_free_ char *d = NULL;
+        uint32_t nr = 0;
+        const char *ip;
+        char **i;
+        int r;
+
+        assert(h);
+        assert(user_record_storage(h) == USER_FSCRYPT);
+        assert(ret_home);
+
+        assert_se(ip = user_record_image_path(h));
+
+        r = tempfn_random(ip, "homework", &d);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate temporary directory: %m");
+
+        (void) mkdir_parents(d, 0755);
+
+        if (mkdir(d, 0700) < 0)
+                return log_error_errno(errno, "Failed to create temporary home directory %s: %m", d);
+
+        temporary = TAKE_PTR(d); /* Needs to be destroyed now */
+
+        root_fd = open(temporary, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+        if (root_fd < 0)
+                return log_error_errno(errno, "Failed to open temporary home directory: %m");
+
+        if (ioctl(root_fd, FS_IOC_GET_ENCRYPTION_POLICY, &policy) < 0) {
+                if (ERRNO_IS_NOT_SUPPORTED(errno)) {
+                        log_error_errno(errno, "File system does not support fscrypt: %m");
+                        return -ENOLINK; /* make recognizable */
+                }
+                if (errno != ENODATA)
+                        return log_error_errno(errno, "Failed to get fscrypt policy of directory: %m");
+        } else
+                return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Parent of %s already encrypted, refusing.", d);
+
+        volume_key = malloc(volume_key_size);
+        if (!volume_key)
+                return log_oom();
+
+        r = genuine_random_bytes(volume_key, volume_key_size, RANDOM_BLOCK);
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire volume key: %m");
+
+        log_info("Generated volume key of size %zu.", volume_key_size);
+
+        policy = (struct fscrypt_policy) {
+                .contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS,
+                .filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS,
+                .flags = FS_POLICY_FLAGS_PAD_32,
+        };
+
+        calculate_key_descriptor(volume_key, volume_key_size, policy.master_key_descriptor);
+
+        r = fscrypt_upload_volume_key(policy.master_key_descriptor, volume_key, volume_key_size, KEY_SPEC_THREAD_KEYRING);
+        if (r < 0)
+                return r;
+
+        log_info("Uploaded volume key to kernel.");
+
+        if (ioctl(root_fd, FS_IOC_SET_ENCRYPTION_POLICY, &policy) < 0)
+                return log_error_errno(errno, "Failed to set fscrypt policy on directory: %m");
+
+        log_info("Encryption policy set.");
+
+        STRV_FOREACH(i, effective_passwords) {
+                r = fscrypt_slot_set(root_fd, volume_key, volume_key_size, *i, nr);
+                if (r < 0)
+                        return r;
+
+                nr++;
+        }
+
+        (void) home_update_quota_classic(h, temporary);
+
+        r = home_populate(h, root_fd);
+        if (r < 0)
+                return r;
+
+        r = home_sync_and_statfs(root_fd, NULL);
+        if (r < 0)
+                return r;
+
+        r = user_record_clone(h, USER_RECORD_LOAD_MASK_SECRET, &new_home);
+        if (r < 0)
+                return log_error_errno(r, "Failed to clone record: %m");
+
+        r = user_record_add_binding(
+                        new_home,
+                        USER_FSCRYPT,
+                        ip,
+                        SD_ID128_NULL,
+                        SD_ID128_NULL,
+                        SD_ID128_NULL,
+                        NULL,
+                        NULL,
+                        UINT64_MAX,
+                        NULL,
+                        NULL,
+                        h->uid,
+                        (gid_t) h->uid);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add binding to record: %m");
+
+        if (rename(temporary, ip) < 0)
+                return log_error_errno(errno, "Failed to rename %s to %s: %m", temporary, ip);
+
+        temporary = mfree(temporary);
+
+        log_info("Everything completed.");
+
+        *ret_home = TAKE_PTR(new_home);
+        return 0;
+}
+
+int home_passwd_fscrypt(
+                UserRecord *h,
+                HomeSetup *setup,
+                char **pkcs11_decrypted_passwords, /* the passwords acquired via PKCS#11 security tokens */
+                char **effective_passwords          /* new passwords */) {
+
+        _cleanup_(erase_and_freep) void *volume_key = NULL;
+        _cleanup_free_ char *xattr_buf = NULL;
+        size_t volume_key_size = 0;
+        uint32_t slot = 0;
+        const char *xa;
+        char **p;
+        int r;
+
+        assert(h);
+        assert(user_record_storage(h) == USER_FSCRYPT);
+        assert(setup);
+
+        r = fscrypt_setup(
+                        pkcs11_decrypted_passwords,
+                        h->password,
+                        setup,
+                        &volume_key,
+                        &volume_key_size);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(p, effective_passwords) {
+                r = fscrypt_slot_set(setup->root_fd, volume_key, volume_key_size, *p, slot);
+                if (r < 0)
+                        return r;
+
+                slot++;
+        }
+
+        r = flistxattr_malloc(setup->root_fd, &xattr_buf);
+        if (r < 0)
+                return log_error_errno(errno, "Failed to retrieve xattr list: %m");
+
+        NULSTR_FOREACH(xa, xattr_buf) {
+                const char *nr;
+                uint32_t z;
+
+                /* Check if this xattr has the format 'trusted.fscrypt_slot<nr>' where '<nr>' is a 32bit unsigned integer */
+                nr = startswith(xa, "trusted.fscrypt_slot");
+                if (!nr)
+                        continue;
+                if (safe_atou32(nr, &z) < 0)
+                        continue;
+
+                if (z < slot)
+                        continue;
+
+                if (fremovexattr(setup->root_fd, xa) < 0)
+
+                        if (errno != ENODATA)
+                                log_warning_errno(errno, "Failed to remove xattr %s: %m", xa);
+        }
+
+        return 0;
+}
diff --git a/src/home/homework-fscrypt.h b/src/home/homework-fscrypt.h
new file mode 100644 (file)
index 0000000..aa3bcd3
--- /dev/null
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "homework.h"
+#include "user-record.h"
+
+int home_prepare_fscrypt(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, 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);
diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c
new file mode 100644 (file)
index 0000000..0cd5902
--- /dev/null
@@ -0,0 +1,2954 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <libfdisk.h>
+#include <linux/loop.h>
+#include <poll.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+
+#include "blkid-util.h"
+#include "blockdev-util.h"
+#include "chattr-util.h"
+#include "dm-util.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "fsck-util.h"
+#include "homework-luks.h"
+#include "homework-mount.h"
+#include "id128-util.h"
+#include "io-util.h"
+#include "memory-util.h"
+#include "missing_magic.h"
+#include "mkdir.h"
+#include "mount-util.h"
+#include "openssl-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "process-util.h"
+#include "random-util.h"
+#include "resize-fs.h"
+#include "stat-util.h"
+#include "strv.h"
+#include "tmpfile-util.h"
+
+/* Round down to the nearest 1K size. Note that Linux generally handles block devices with 512 blocks only,
+ * but actually doesn't accept uneven numbers in many cases. To avoid any confusion around this we'll
+ * 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,
+                sd_id128_t *ret_uuid) {
+
+        _cleanup_(blkid_free_probep) blkid_probe b = NULL;
+        _cleanup_free_ char *s = NULL;
+        const char *fstype = NULL, *uuid = NULL;
+        sd_id128_t id;
+        int r;
+
+        assert(fd >= 0);
+        assert(ret_fstype);
+        assert(ret_uuid);
+
+        b = blkid_new_probe();
+        if (!b)
+                return -ENOMEM;
+
+        errno = 0;
+        r = blkid_probe_set_device(b, fd, 0, 0);
+        if (r != 0)
+                return errno > 0 ? -errno : -ENOMEM;
+
+        (void) blkid_probe_enable_superblocks(b, 1);
+        (void) blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_UUID);
+
+        errno = 0;
+        r = blkid_do_safeprobe(b);
+        if (IN_SET(r, -2, 1)) /* nothing found or ambiguous result */
+                return -ENOPKG;
+        if (r != 0)
+                return errno > 0 ? -errno : -EIO;
+
+        (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
+        if (!fstype)
+                return -ENOPKG;
+
+        (void) blkid_probe_lookup_value(b, "UUID", &uuid, NULL);
+        if (!uuid)
+                return -ENOPKG;
+
+        r = sd_id128_from_string(uuid, &id);
+        if (r < 0)
+                return r;
+
+        s = strdup(fstype);
+        if (!s)
+                return -ENOMEM;
+
+        *ret_fstype = TAKE_PTR(s);
+        *ret_uuid = id;
+
+        return 0;
+}
+
+static int probe_file_system_by_path(const char *path, char **ret_fstype, sd_id128_t *ret_uuid) {
+        _cleanup_close_ int fd = -1;
+
+        fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+        if (fd < 0)
+                return -errno;
+
+        return probe_file_system_by_fd(fd, ret_fstype, ret_uuid);
+}
+
+static int block_get_size_by_fd(int fd, uint64_t *ret) {
+        struct stat st;
+
+        assert(fd >= 0);
+        assert(ret);
+
+        if (fstat(fd, &st) < 0)
+                return -errno;
+
+        if (!S_ISBLK(st.st_mode))
+                return -ENOTBLK;
+
+        if (ioctl(fd, BLKGETSIZE64, ret) < 0)
+                return -errno;
+
+        return 0;
+}
+
+static int block_get_size_by_path(const char *path, uint64_t *ret) {
+        _cleanup_close_ int fd = -1;
+
+        fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+        if (fd < 0)
+                return -errno;
+
+        return block_get_size_by_fd(fd, ret);
+}
+
+static int run_fsck(const char *node, const char *fstype) {
+        int r, exit_status;
+        pid_t fsck_pid;
+
+        assert(node);
+        assert(fstype);
+
+        r = fsck_exists(fstype);
+        if (r < 0)
+                return log_error_errno(r, "Failed to check if fsck for file system %s exists: %m", fstype);
+        if (r == 0) {
+                log_warning("No fsck for file system %s installed, ignoring.", fstype);
+                return 0;
+        }
+
+        r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_STDOUT_TO_STDERR, &fsck_pid);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                /* Child */
+                execl("/sbin/fsck", "/sbin/fsck", "-aTl", node, NULL);
+                log_error_errno(errno, "Failed to execute fsck: %m");
+                _exit(FSCK_OPERATIONAL_ERROR);
+        }
+
+        exit_status = wait_for_terminate_and_check("fsck", fsck_pid, WAIT_LOG_ABNORMAL);
+        if (exit_status < 0)
+                return exit_status;
+        if ((exit_status & ~FSCK_ERROR_CORRECTED) != 0) {
+                log_warning("fsck failed with exit status %i.", exit_status);
+
+                if ((exit_status & (FSCK_SYSTEM_SHOULD_REBOOT|FSCK_ERRORS_LEFT_UNCORRECTED)) != 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "File system is corrupted, refusing.");
+
+                log_warning("Ignoring fsck error.");
+        }
+
+        log_info("File system check completed.");
+
+        return 1;
+}
+
+static int luks_try_passwords(
+                struct crypt_device *cd,
+                char **passwords,
+                void *volume_key,
+                size_t *volume_key_size) {
+
+        char **pp;
+        int r;
+
+        assert(cd);
+
+        STRV_FOREACH(pp, passwords) {
+                size_t vks = *volume_key_size;
+
+                r = crypt_volume_key_get(
+                                cd,
+                                CRYPT_ANY_SLOT,
+                                volume_key,
+                                &vks,
+                                *pp,
+                                strlen(*pp));
+                if (r >= 0) {
+                        *volume_key_size = vks;
+                        return 0;
+                }
+
+                log_debug_errno(r, "Password %zu didn't work for unlocking LUKS superblock: %m", (size_t) (pp - passwords));
+        }
+
+        return -ENOKEY;
+}
+
+static int luks_setup(
+                const char *node,
+                const char *dm_name,
+                sd_id128_t uuid,
+                const char *cipher,
+                const char *cipher_mode,
+                uint64_t volume_key_size,
+                char **passwords,
+                char **pkcs11_decrypted_passwords,
+                bool discard,
+                struct crypt_device **ret,
+                sd_id128_t *ret_found_uuid,
+                void **ret_volume_key,
+                size_t *ret_volume_key_size) {
+
+        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_(erase_and_freep) void *vk = NULL;
+        sd_id128_t p;
+        size_t vks;
+        int r;
+
+        assert(node);
+        assert(dm_name);
+        assert(ret);
+
+        r = crypt_init(&cd, node);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate libcryptsetup context: %m");
+
+        crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
+
+        r = crypt_load(cd, CRYPT_LUKS2, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to load LUKS superblock: %m");
+
+        r = crypt_get_volume_key_size(cd);
+        if (r <= 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine LUKS volume key size");
+        vks = (size_t) r;
+
+        if (!sd_id128_is_null(uuid) || ret_found_uuid) {
+                const char *s;
+
+                s = crypt_get_uuid(cd);
+                if (!s)
+                        return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock has no UUID.");
+
+                r = sd_id128_from_string(s, &p);
+                if (r < 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock has invalid UUID.");
+
+                /* Check that the UUID matches, if specified */
+                if (!sd_id128_is_null(uuid) &&
+                    !sd_id128_equal(uuid, p))
+                        return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock has wrong UUID.");
+        }
+
+        if (cipher && !streq_ptr(cipher, crypt_get_cipher(cd)))
+                return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock declares wrong cipher.");
+
+        if (cipher_mode && !streq_ptr(cipher_mode, crypt_get_cipher_mode(cd)))
+                return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock declares wrong cipher mode.");
+
+        if (volume_key_size != UINT64_MAX && vks != volume_key_size)
+                return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock declares wrong volume key size.");
+
+        vk = malloc(vks);
+        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.");
+        }
+        if (r < 0)
+                return log_error_errno(r, "Failed to unlocks LUKS superblock: %m");
+
+        r = crypt_activate_by_volume_key(
+                        cd,
+                        dm_name,
+                        vk, vks,
+                        discard ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to unlock LUKS superblock: %m");
+
+        log_info("Setting up LUKS device /dev/mapper/%s completed.", dm_name);
+
+        *ret = TAKE_PTR(cd);
+
+        if (ret_found_uuid) /* Return the UUID actually found if the caller wants to know */
+                *ret_found_uuid = p;
+        if (ret_volume_key)
+                *ret_volume_key = TAKE_PTR(vk);
+        if (ret_volume_key_size)
+                *ret_volume_key_size = vks;
+
+        return 0;
+}
+
+static int luks_open(
+                const char *dm_name,
+                char **passwords,
+                char **pkcs11_decrypted_passwords,
+                struct crypt_device **ret,
+                sd_id128_t *ret_found_uuid,
+                void **ret_volume_key,
+                size_t *ret_volume_key_size) {
+
+        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_(erase_and_freep) void *vk = NULL;
+        sd_id128_t p;
+        size_t vks;
+        int r;
+
+        assert(dm_name);
+        assert(ret);
+
+        /* Opens a LUKS device that is already set up. Re-validates the password while doing so (which also
+         * provides us with the volume key, which we want). */
+
+        r = crypt_init_by_name(&cd, dm_name);
+        if (r < 0)
+                return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", dm_name);
+
+        crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
+
+        r = crypt_load(cd, CRYPT_LUKS2, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to load LUKS superblock: %m");
+
+        r = crypt_get_volume_key_size(cd);
+        if (r <= 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine LUKS volume key size");
+        vks = (size_t) r;
+
+        if (ret_found_uuid) {
+                const char *s;
+
+                s = crypt_get_uuid(cd);
+                if (!s)
+                        return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock has no UUID.");
+
+                r = sd_id128_from_string(s, &p);
+                if (r < 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock has invalid UUID.");
+        }
+
+        vk = malloc(vks);
+        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.");
+        }
+        if (r < 0)
+                return log_error_errno(r, "Failed to unlocks LUKS superblock: %m");
+
+        log_info("Discovered used LUKS device /dev/mapper/%s, and validated password.", dm_name);
+
+        /* This is needed so that crypt_resize() can operate correctly for pre-existing LUKS devices. We need
+         * to tell libcryptsetup the volume key explicitly, so that it is in the kernel keyring. */
+        r = crypt_activate_by_volume_key(cd, NULL, vk, vks, CRYPT_ACTIVATE_KEYRING_KEY);
+        if (r < 0)
+                return log_error_errno(r, "Failed to upload volume key again: %m");
+
+        log_info("Successfully re-activated LUKS device.");
+
+        *ret = TAKE_PTR(cd);
+
+        if (ret_found_uuid)
+                *ret_found_uuid = p;
+        if (ret_volume_key)
+                *ret_volume_key = TAKE_PTR(vk);
+        if (ret_volume_key_size)
+                *ret_volume_key_size = vks;
+
+        return 0;
+}
+
+static int fs_validate(
+                const char *dm_node,
+                sd_id128_t uuid,
+                char **ret_fstype,
+                sd_id128_t *ret_found_uuid) {
+
+        _cleanup_free_ char *fstype = NULL;
+        sd_id128_t u;
+        int r;
+
+        assert(dm_node);
+        assert(ret_fstype);
+
+        r = probe_file_system_by_path(dm_node, &fstype, &u);
+        if (r < 0)
+                return log_error_errno(r, "Failed to probe file system: %m");
+
+        /* 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. */
+        if (!supported_fstype(fstype))
+                return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "Image contains unsupported file system: %s", strna(fstype));
+
+        if (!sd_id128_is_null(uuid) &&
+            !sd_id128_equal(uuid, u))
+                return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "File system has wrong UUID.");
+
+        log_info("Probing file system completed (found %s).", fstype);
+
+        *ret_fstype = TAKE_PTR(fstype);
+
+        if (ret_found_uuid) /* Return the UUID actually found if the caller wants to know */
+                *ret_found_uuid = u;
+
+        return 0;
+}
+
+static int make_dm_names(const char *user_name, char **ret_dm_name, char **ret_dm_node) {
+        _cleanup_free_ char *name = NULL, *node = NULL;
+
+        assert(user_name);
+        assert(ret_dm_name);
+        assert(ret_dm_node);
+
+        name = strjoin("home-", user_name);
+        if (!name)
+                return log_oom();
+
+        node = path_join("/dev/mapper/", name);
+        if (!node)
+                return log_oom();
+
+        *ret_dm_name = TAKE_PTR(name);
+        *ret_dm_node = TAKE_PTR(node);
+        return 0;
+}
+
+static int luks_validate(
+                int fd,
+                const char *label,
+                sd_id128_t partition_uuid,
+                sd_id128_t *ret_partition_uuid,
+                uint64_t *ret_offset,
+                uint64_t *ret_size) {
+
+        _cleanup_(blkid_free_probep) blkid_probe b = NULL;
+        sd_id128_t found_partition_uuid = SD_ID128_NULL;
+        const char *fstype = NULL, *pttype = NULL;
+        blkid_loff_t offset = 0, size = 0;
+        blkid_partlist pl;
+        bool found = false;
+        int r, i, n;
+
+        assert(fd >= 0);
+        assert(label);
+        assert(ret_offset);
+        assert(ret_size);
+
+        b = blkid_new_probe();
+        if (!b)
+                return -ENOMEM;
+
+        errno = 0;
+        r = blkid_probe_set_device(b, fd, 0, 0);
+        if (r != 0)
+                return errno > 0 ? -errno : -ENOMEM;
+
+        (void) blkid_probe_enable_superblocks(b, 1);
+        (void) blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
+        (void) blkid_probe_enable_partitions(b, 1);
+        (void) blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
+
+        errno = 0;
+        r = blkid_do_safeprobe(b);
+        if (IN_SET(r, -2, 1)) /* nothing found or ambiguous result */
+                return -ENOPKG;
+        if (r != 0)
+                return errno > 0 ? -errno : -EIO;
+
+        (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
+        if (streq_ptr(fstype, "crypto_LUKS")) {
+                /* Directly a LUKS image */
+                *ret_offset = 0;
+                *ret_size = UINT64_MAX; /* full disk */
+                *ret_partition_uuid = SD_ID128_NULL;
+                return 0;
+        } else if (fstype)
+                return -ENOPKG;
+
+        (void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
+        if (!streq_ptr(pttype, "gpt"))
+                return -ENOPKG;
+
+        errno = 0;
+        pl = blkid_probe_get_partitions(b);
+        if (!pl)
+                return errno > 0 ? -errno : -ENOMEM;
+
+        errno = 0;
+        n = blkid_partlist_numof_partitions(pl);
+        if (n < 0)
+                return errno > 0 ? -errno : -EIO;
+
+        for (i = 0; i < n; i++) {
+                blkid_partition pp;
+                sd_id128_t id;
+                const char *sid;
+
+                errno = 0;
+                pp = blkid_partlist_get_partition(pl, i);
+                if (!pp)
+                        return errno > 0 ? -errno : -EIO;
+
+                if (!streq_ptr(blkid_partition_get_type_string(pp), "773f91ef-66d4-49b5-bd83-d683bf40ad16"))
+                        continue;
+
+                if (!streq_ptr(blkid_partition_get_name(pp), label))
+                        continue;
+
+                sid = blkid_partition_get_uuid(pp);
+                if (sid) {
+                        r = sd_id128_from_string(sid, &id);
+                        if (r < 0)
+                                log_debug_errno(r, "Couldn't parse partition UUID %s, weird: %m", sid);
+
+                        if (!sd_id128_is_null(partition_uuid) && !sd_id128_equal(id, partition_uuid))
+                                continue;
+                }
+
+                if (found)
+                        return -ENOPKG;
+
+                offset = blkid_partition_get_start(pp);
+                size = blkid_partition_get_size(pp);
+                found_partition_uuid = id;
+
+                found = true;
+        }
+
+        if (!found)
+                return -ENOPKG;
+
+        if (offset < 0)
+                return -EINVAL;
+        if ((uint64_t) offset > UINT64_MAX / 512U)
+                return -EINVAL;
+        if (size <= 0)
+                return -EINVAL;
+        if ((uint64_t) size > UINT64_MAX / 512U)
+                return -EINVAL;
+
+        *ret_offset = offset * 512U;
+        *ret_size = size * 512U;
+        *ret_partition_uuid = found_partition_uuid;
+
+        return 0;
+}
+
+static int crypt_device_to_evp_cipher(struct crypt_device *cd, const EVP_CIPHER **ret) {
+        _cleanup_free_ char *cipher_name = NULL;
+        const char *cipher, *cipher_mode, *e;
+        size_t key_size, key_bits;
+        const EVP_CIPHER *cc;
+        int r;
+
+        assert(cd);
+
+        /* Let's find the right OpenSSL EVP_CIPHER object that matches the encryption settings of the LUKS
+         * device */
+
+        cipher = crypt_get_cipher(cd);
+        if (!cipher)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot get cipher from LUKS device.");
+
+        cipher_mode = crypt_get_cipher_mode(cd);
+        if (!cipher_mode)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot get cipher mode from LUKS device.");
+
+        e = strchr(cipher_mode, '-');
+        if (e)
+                cipher_mode = strndupa(cipher_mode, e - cipher_mode);
+
+        r = crypt_get_volume_key_size(cd);
+        if (r <= 0)
+                return log_error_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL), "Cannot get volume key size from LUKS device.");
+
+        key_size = r;
+        key_bits = key_size * 8;
+        if (streq(cipher_mode, "xts"))
+                key_bits /= 2;
+
+        if (asprintf(&cipher_name, "%s-%zu-%s", cipher, key_bits, cipher_mode) < 0)
+                return log_oom();
+
+        cc = EVP_get_cipherbyname(cipher_name);
+        if (!cc)
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Selected cipher mode '%s' not supported, can't encrypt JSON record.", cipher_name);
+
+        /* Verify that our key length calculations match what OpenSSL thinks */
+        r = EVP_CIPHER_key_length(cc);
+        if (r < 0 || (uint64_t) r != key_size)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key size of selected cipher doesn't meet out expectations.");
+
+        *ret = cc;
+        return 0;
+}
+
+static int luks_validate_home_record(
+                struct crypt_device *cd,
+                UserRecord *h,
+                const void *volume_key,
+                char ***pkcs11_decrypted_passwords,
+                UserRecord **ret_luks_home_record) {
+
+        int r, token;
+
+        assert(cd);
+        assert(h);
+
+        for (token = 0;; token++) {
+                _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *rr = NULL;
+                _cleanup_(EVP_CIPHER_CTX_freep) EVP_CIPHER_CTX *context = NULL;
+                _cleanup_(user_record_unrefp) UserRecord *lhr = NULL;
+                _cleanup_free_ void *encrypted = NULL, *iv = NULL;
+                size_t decrypted_size, encrypted_size, iv_size;
+                int decrypted_size_out1, decrypted_size_out2;
+                _cleanup_free_ char *decrypted = NULL;
+                const char *text, *type;
+                crypt_token_info state;
+                JsonVariant *jr, *jiv;
+                unsigned line, column;
+                const EVP_CIPHER *cc;
+
+                state = crypt_token_status(cd, token, &type);
+                if (state == CRYPT_TOKEN_INACTIVE) /* First unconfigured token, give up */
+                        break;
+                if (IN_SET(state, CRYPT_TOKEN_INTERNAL, CRYPT_TOKEN_INTERNAL_UNKNOWN, CRYPT_TOKEN_EXTERNAL))
+                        continue;
+                if (state != CRYPT_TOKEN_EXTERNAL_UNKNOWN)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected token state of token %i: %i", token, (int) state);
+
+                if (!streq(type, "systemd-homed"))
+                        continue;
+
+                r = crypt_token_json_get(cd, token, &text);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to read LUKS token %i: %m", token);
+
+                r = json_parse(text, JSON_PARSE_SENSITIVE, &v, &line, &column);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse LUKS token JSON data %u:%u: %m", line, column);
+
+                jr = json_variant_by_key(v, "record");
+                if (!jr)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "LUKS token lacks 'record' field.");
+                jiv = json_variant_by_key(v, "iv");
+                if (!jiv)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "LUKS token lacks 'iv' field.");
+
+                r = json_variant_unbase64(jr, &encrypted, &encrypted_size);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to base64 decode record: %m");
+
+                r = json_variant_unbase64(jiv, &iv, &iv_size);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to base64 decode IV: %m");
+
+                r = crypt_device_to_evp_cipher(cd, &cc);
+                if (r < 0)
+                        return r;
+                if (iv_size > INT_MAX || EVP_CIPHER_iv_length(cc) != (int) iv_size)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "IV size doesn't match.");
+
+                context = EVP_CIPHER_CTX_new();
+                if (!context)
+                        return log_oom();
+
+                if (EVP_DecryptInit_ex(context, cc, NULL, volume_key, iv) != 1)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize decryption context.");
+
+                decrypted_size = encrypted_size + EVP_CIPHER_key_length(cc) * 2;
+                decrypted = new(char, decrypted_size);
+                if (!decrypted)
+                        return log_oom();
+
+                if (EVP_DecryptUpdate(context, (uint8_t*) decrypted, &decrypted_size_out1, encrypted, encrypted_size) != 1)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to decrypt JSON record.");
+
+                assert((size_t) decrypted_size_out1 <= decrypted_size);
+
+                if (EVP_DecryptFinal_ex(context, (uint8_t*) decrypted + decrypted_size_out1, &decrypted_size_out2) != 1)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finish decryption of JSON record.");
+
+                assert((size_t) decrypted_size_out1 + (size_t) decrypted_size_out2 < decrypted_size);
+                decrypted_size = (size_t) decrypted_size_out1 + (size_t) decrypted_size_out2;
+
+                if (memchr(decrypted, 0, decrypted_size))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Inner NUL byte in JSON record, refusing.");
+
+                decrypted[decrypted_size] = 0;
+
+                r = json_parse(decrypted, JSON_PARSE_SENSITIVE, &rr, NULL, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse decrypted JSON record, refusing.");
+
+                lhr = user_record_new();
+                if (!lhr)
+                        return log_oom();
+
+                r = user_record_load(lhr, rr, USER_RECORD_LOAD_EMBEDDED);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse user record: %m");
+
+                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);
+                if (r < 0)
+                        return r;
+
+                *ret_luks_home_record = TAKE_PTR(lhr);
+                return 0;
+        }
+
+        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Couldn't find home record in LUKS2 header, refusing.");
+}
+
+static int format_luks_token_text(
+                struct crypt_device *cd,
+                UserRecord *hr,
+                const void *volume_key,
+                char **ret) {
+
+        int r, encrypted_size_out1 = 0, encrypted_size_out2 = 0, iv_size, key_size;
+        _cleanup_(EVP_CIPHER_CTX_freep) EVP_CIPHER_CTX *context = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        _cleanup_free_ void *iv = NULL, *encrypted = NULL;
+        size_t text_length, encrypted_size;
+        _cleanup_free_ char *text = NULL;
+        const EVP_CIPHER *cc;
+
+        assert(cd);
+        assert(hr);
+        assert(volume_key);
+        assert(ret);
+
+        r = crypt_device_to_evp_cipher(cd, &cc);
+        if (r < 0)
+                return r;
+
+        key_size = EVP_CIPHER_key_length(cc);
+        iv_size = EVP_CIPHER_iv_length(cc);
+
+        if (iv_size > 0) {
+                iv = malloc(iv_size);
+                if (!iv)
+                        return log_oom();
+
+                r = genuine_random_bytes(iv, iv_size, RANDOM_BLOCK);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to generate IV: %m");
+        }
+
+        context = EVP_CIPHER_CTX_new();
+        if (!context)
+                return log_oom();
+
+        if (EVP_EncryptInit_ex(context, cc, NULL, volume_key, iv) != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize encryption context.");
+
+        r = json_variant_format(hr->json, 0, &text);
+        if (r < 0)
+                return log_error_errno(r,"Failed to format user record for LUKS: %m");
+
+        text_length = strlen(text);
+        encrypted_size = text_length + 2*key_size - 1;
+
+        encrypted = malloc(encrypted_size);
+        if (!encrypted)
+                return log_oom();
+
+        if (EVP_EncryptUpdate(context, encrypted, &encrypted_size_out1, (uint8_t*) text, text_length) != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to encrypt JSON record.");
+
+        assert((size_t) encrypted_size_out1 <= encrypted_size);
+
+        if (EVP_EncryptFinal_ex(context, (uint8_t*) encrypted + encrypted_size_out1, &encrypted_size_out2) != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finish encryption of JSON record. ");
+
+        assert((size_t) encrypted_size_out1 + (size_t) encrypted_size_out2 <= encrypted_size);
+
+        r = json_build(&v,
+                       JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR("type", JSON_BUILD_STRING("systemd-homed")),
+                                       JSON_BUILD_PAIR("keyslots", JSON_BUILD_EMPTY_ARRAY),
+                                       JSON_BUILD_PAIR("record", JSON_BUILD_BASE64(encrypted, encrypted_size_out1 + encrypted_size_out2)),
+                                       JSON_BUILD_PAIR("iv", JSON_BUILD_BASE64(iv, iv_size))));
+        if (r < 0)
+                return log_error_errno(r, "Failed to prepare LUKS JSON token object: %m");
+
+        r = json_variant_format(v, 0, ret);
+        if (r < 0)
+                return log_error_errno(r, "Failed to format encrypted user record for LUKS: %m");
+
+        return 0;
+}
+
+int home_store_header_identity_luks(
+                UserRecord *h,
+                HomeSetup *setup,
+                UserRecord *old_home) {
+
+        _cleanup_(user_record_unrefp) UserRecord *header_home = NULL;
+        _cleanup_free_ char *text = NULL;
+        int token = 0, r;
+
+        assert(h);
+
+        if (!setup->crypt_device)
+                return 0;
+
+        assert(setup->volume_key);
+
+        /* Let's store the user's identity record in the LUKS2 "token" header data fields, in an encrypted
+         * fashion. Why that? If we'd rely on the record being embedded in the payload file system itself we
+         * would have to mount the file system before we can validate the JSON record, its signatures and
+         * whether it matches what we are looking for. However, kernel file system implementations are
+         * generally not ready to be used on untrusted media. Hence let's store the record independently of
+         * the file system, so that we can validate it first, and only then mount the file system. To keep
+         * things simple we use the same encryption settings for this record as for the file system itself. */
+
+        r = user_record_clone(h, USER_RECORD_EXTRACT_EMBEDDED, &header_home);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine new header record: %m");
+
+        if (old_home && user_record_equal(old_home, header_home)) {
+                log_debug("Not updating header home record.");
+                return 0;
+        }
+
+        r = format_luks_token_text(setup->crypt_device, header_home, setup->volume_key, &text);
+        if (r < 0)
+                return r;
+
+        for (;; token++) {
+                crypt_token_info state;
+                const char *type;
+
+                state = crypt_token_status(setup->crypt_device, token, &type);
+                if (state == CRYPT_TOKEN_INACTIVE) /* First unconfigured token, we are done */
+                        break;
+                if (IN_SET(state, CRYPT_TOKEN_INTERNAL, CRYPT_TOKEN_INTERNAL_UNKNOWN, CRYPT_TOKEN_EXTERNAL))
+                        continue; /* Not ours */
+                if (state != CRYPT_TOKEN_EXTERNAL_UNKNOWN)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected token state of token %i: %i", token, (int) state);
+
+                if (!streq(type, "systemd-homed"))
+                        continue;
+
+                r = crypt_token_json_set(setup->crypt_device, token, text);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set JSON token for slot %i: %m", token);
+
+                /* Now, let's free the text so that for all further matching tokens we all crypt_json_token_set()
+                 * with a NULL text in order to invalidate the tokens. */
+                text = mfree(text);
+                token++;
+        }
+
+        if (text)
+                return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Didn't find any record token to update.");
+
+        log_info("Wrote LUKS header user record.");
+
+        return 1;
+}
+
+static 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. */
+
+        assert(root_fd >= 0);
+
+        if (ioctl(root_fd, FITRIM, &range) < 0) {
+                if (IN_SET(errno, ENOTTY, EOPNOTSUPP, EBADF)) {
+                        log_debug_errno(errno, "File system does not support FITRIM, not trimming.");
+                        return 0;
+                }
+
+                return log_warning_errno(errno, "Failed to invoke FITRIM, ignoring: %m");
+        }
+
+        log_info("Discarded unused %s.",
+                 format_bytes(buf, sizeof(buf), range.len));
+        return 1;
+}
+
+static int run_fallocate(int backing_fd, const struct stat *st) {
+        char buf[FORMAT_BYTES_MAX];
+
+        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 (!S_ISREG(st->st_mode))
+                return 0;
+
+        if (st->st_blocks >= DIV_ROUND_UP(st->st_size, 512)) {
+                log_info("Backing file is fully allocated already.");
+                return 0;
+        }
+
+        if (fallocate(backing_fd, FALLOC_FL_KEEP_SIZE, 0, st->st_size) < 0) {
+
+                if (ERRNO_IS_NOT_SUPPORTED(errno)) {
+                        log_debug_errno(errno, "fallocate() not supported on file system, ignoring.");
+                        return 0;
+                }
+
+                if (ERRNO_IS_DISK_SPACE(errno)) {
+                        log_debug_errno(errno, "Not enough disk space to fully allocate home.");
+                        return -ENOSPC; /* make recognizable */
+                }
+
+                return log_error_errno(errno, "Failed to allocate backing file blocks: %m");
+        }
+
+        log_info("Allocated additional %s.",
+                 format_bytes(buf, sizeof(buf), (DIV_ROUND_UP(st->st_size, 512) - st->st_blocks) * 512));
+        return 1;
+}
+
+int home_prepare_luks(
+                UserRecord *h,
+                bool already_activated,
+                const char *force_image_path,
+                char ***pkcs11_decrypted_passwords,
+                HomeSetup *setup,
+                UserRecord **ret_luks_home) {
+
+        sd_id128_t found_partition_uuid, found_luks_uuid, found_fs_uuid;
+        _cleanup_(user_record_unrefp) UserRecord *luks_home = NULL;
+        _cleanup_(loop_device_unrefp) LoopDevice *loop = NULL;
+        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_(erase_and_freep) void *volume_key = NULL;
+        bool dm_activated = false, mounted = false;
+        _cleanup_close_ int root_fd = -1;
+        size_t volume_key_size = 0;
+        uint64_t offset, size;
+        int r;
+
+        assert(h);
+        assert(setup);
+        assert(setup->dm_name);
+        assert(setup->dm_node);
+
+        assert(user_record_storage(h) == USER_LUKS);
+
+        if (already_activated) {
+                struct loop_info64 info;
+                const char *n;
+
+                r = luks_open(setup->dm_name,
+                              h->password,
+                              pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL,
+                              &cd,
+                              &found_luks_uuid,
+                              &volume_key,
+                              &volume_key_size);
+                if (r < 0)
+                        return r;
+
+                r = luks_validate_home_record(cd, h, volume_key, pkcs11_decrypted_passwords, &luks_home);
+                if (r < 0)
+                        return r;
+
+                n = crypt_get_device_name(cd);
+                if (!n)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine backing device for DM %s.", setup->dm_name);
+
+                r = loop_device_open(n, O_RDWR, &loop);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to open loopback device %s: %m", n);
+
+                if (ioctl(loop->fd, LOOP_GET_STATUS64, &info) < 0) {
+                        _cleanup_free_ char *sysfs = NULL;
+                        struct stat st;
+
+                        if (!IN_SET(errno, ENOTTY, EINVAL))
+                                return log_error_errno(errno, "Failed to get block device metrics of %s: %m", n);
+
+                        if (ioctl(loop->fd, BLKGETSIZE64, &size) < 0)
+                                return log_error_errno(r, "Failed to read block device size of %s: %m", n);
+
+                        if (fstat(loop->fd, &st) < 0)
+                                return log_error_errno(r, "Failed to stat block device %s: %m", n);
+                        assert(S_ISBLK(st.st_mode));
+
+                        if (asprintf(&sysfs, "/sys/dev/block/%u:%u/partition", major(st.st_rdev), minor(st.st_rdev)) < 0)
+                                return log_oom();
+
+                        if (access(sysfs, F_OK) < 0) {
+                                if (errno != ENOENT)
+                                        return log_error_errno(errno, "Failed to determine whether %s exists: %m", sysfs);
+
+                                offset = 0;
+                        } else {
+                                _cleanup_free_ char *buffer = NULL;
+
+                                if (asprintf(&sysfs, "/sys/dev/block/%u:%u/start", major(st.st_rdev), minor(st.st_rdev)) < 0)
+                                        return log_oom();
+
+                                r = read_one_line_file(sysfs, &buffer);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to read partition start offset: %m");
+
+                                r = safe_atou64(buffer, &offset);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse partition start offset: %m");
+
+                                if (offset > UINT64_MAX / 512U)
+                                        return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "Offset too large for 64 byte range, refusing.");
+
+                                offset *= 512U;
+                        }
+                } else {
+                        offset = info.lo_offset;
+                        size = info.lo_sizelimit;
+                }
+
+                found_partition_uuid = found_fs_uuid = SD_ID128_NULL;
+
+                log_info("Discovered used loopback device %s.", loop->node);
+
+                root_fd = open(user_record_home_directory(h), O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+                if (root_fd < 0) {
+                        r = log_error_errno(r, "Failed to open home directory: %m");
+                        goto fail;
+                }
+        } else {
+                _cleanup_free_ char *fstype = NULL, *subdir = NULL;
+                _cleanup_close_ int fd = -1;
+                const char *ip;
+                struct stat st;
+
+                ip = force_image_path ?: user_record_image_path(h);
+
+                subdir = path_join("/run/systemd/user-home-mount/", user_record_user_name_and_realm(h));
+                if (!subdir)
+                        return log_oom();
+
+                fd = open(ip, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+                if (fd < 0)
+                        return log_error_errno(errno, "Failed to open image file %s: %m", ip);
+
+                if (fstat(fd, &st) < 0)
+                        return log_error_errno(errno, "Failed to fstat() image file: %m");
+                if (!S_ISREG(st.st_mode) && !S_ISBLK(st.st_mode))
+                        return log_error_errno(errno, "Image file %s is not a regular file or block device: %m", ip);
+
+                r = luks_validate(fd, user_record_user_name_and_realm(h), h->partition_uuid, &found_partition_uuid, &offset, &size);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to validate disk label: %m");
+
+                if (!user_record_luks_discard(h)) {
+                        r = run_fallocate(fd, &st);
+                        if (r < 0)
+                                return r;
+                }
+
+                r = loop_device_make(fd, O_RDWR, offset, size, 0, &loop);
+                if (r == -ENOENT) {
+                        log_error_errno(r, "Loopback block device support is not available on this system.");
+                        return -ENOLINK; /* make recognizable */
+                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to allocate loopback context: %m");
+
+                log_info("Setting up loopback device %s completed.", loop->node ?: ip);
+
+                r = luks_setup(loop->node ?: ip,
+                               setup->dm_name,
+                               h->luks_uuid,
+                               h->luks_cipher,
+                               h->luks_cipher_mode,
+                               h->luks_volume_key_size,
+                               h->password,
+                               pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL,
+                               user_record_luks_discard(h),
+                               &cd,
+                               &found_luks_uuid,
+                               &volume_key,
+                               &volume_key_size);
+                if (r < 0)
+                        return r;
+
+                dm_activated = true;
+
+                r = luks_validate_home_record(cd, h, volume_key, pkcs11_decrypted_passwords, &luks_home);
+                if (r < 0)
+                        goto fail;
+
+                r = fs_validate(setup->dm_node, h->file_system_uuid, &fstype, &found_fs_uuid);
+                if (r < 0)
+                        goto fail;
+
+                r = run_fsck(setup->dm_node, fstype);
+                if (r < 0)
+                        goto fail;
+
+                r = home_unshare_and_mount(setup->dm_node, fstype, user_record_luks_discard(h));
+                if (r < 0)
+                        goto fail;
+
+                mounted = true;
+
+                root_fd = open(subdir, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+                if (root_fd < 0) {
+                        r = log_error_errno(r, "Failed to open home directory: %m");
+                        goto fail;
+                }
+
+                if (user_record_luks_discard(h))
+                        (void) run_fitrim(root_fd);
+        }
+
+        setup->loop = TAKE_PTR(loop);
+        setup->crypt_device = TAKE_PTR(cd);
+        setup->root_fd = TAKE_FD(root_fd);
+        setup->found_partition_uuid = found_partition_uuid;
+        setup->found_luks_uuid = found_luks_uuid;
+        setup->found_fs_uuid = found_fs_uuid;
+        setup->partition_offset = offset;
+        setup->partition_size = size;
+        setup->volume_key = TAKE_PTR(volume_key);
+        setup->volume_key_size = volume_key_size;
+
+        setup->undo_mount = mounted;
+        setup->undo_dm = dm_activated;
+
+        if (ret_luks_home)
+                *ret_luks_home = TAKE_PTR(luks_home);
+
+        return 0;
+
+fail:
+        if (mounted)
+                (void) umount_verbose("/run/systemd/user-home-mount");
+
+        if (dm_activated)
+                (void) crypt_deactivate(cd, setup->dm_name);
+
+        return r;
+}
+
+static void print_size_summary(uint64_t host_size, uint64_t encrypted_size, struct statfs *sfs) {
+        char buffer1[FORMAT_BYTES_MAX], buffer2[FORMAT_BYTES_MAX], buffer3[FORMAT_BYTES_MAX], buffer4[FORMAT_BYTES_MAX];
+
+        assert(sfs);
+
+        log_info("Image size is %s, file system size is %s, file system payload size is %s, file system free is %s.",
+                 format_bytes(buffer1, sizeof(buffer1), host_size),
+                 format_bytes(buffer2, sizeof(buffer2), encrypted_size),
+                 format_bytes(buffer3, sizeof(buffer3), (uint64_t) sfs->f_blocks * (uint64_t) sfs->f_frsize),
+                 format_bytes(buffer4, sizeof(buffer4), (uint64_t) sfs->f_bfree * (uint64_t) sfs->f_frsize));
+}
+
+int home_activate_luks(
+                UserRecord *h,
+                char ***pkcs11_decrypted_passwords,
+                UserRecord **ret_home) {
+
+        _cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *luks_home_record = NULL;
+        _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+        uint64_t host_size, encrypted_size;
+        const char *hdo, *hd;
+        struct statfs sfs;
+        int r;
+
+        assert(h);
+        assert(user_record_storage(h) == USER_LUKS);
+        assert(ret_home);
+
+        assert_se(hdo = user_record_home_directory(h));
+        hd = strdupa(hdo); /* copy the string out, since it might change later in the home record object */
+
+        r = make_dm_names(h->user_name, &setup.dm_name, &setup.dm_node);
+        if (r < 0)
+                return r;
+
+        r = access(setup.dm_node, F_OK);
+        if (r < 0) {
+                if (errno != ENOENT)
+                        return log_error_errno(errno, "Failed to determine whether %s exists: %m", setup.dm_node);
+        } else
+                return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Device mapper device %s already exists, refusing.", setup.dm_node);
+
+        r = home_prepare_luks(
+                        h,
+                        false,
+                        NULL,
+                        pkcs11_decrypted_passwords,
+                        &setup,
+                        &luks_home_record);
+        if (r < 0)
+                return r;
+
+        r = block_get_size_by_fd(setup.loop->fd, &host_size);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get loopback block device size: %m");
+
+        r = block_get_size_by_path(setup.dm_node, &encrypted_size);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get LUKS block device size: %m");
+
+        r = home_refresh(
+                        h,
+                        &setup,
+                        luks_home_record,
+                        pkcs11_decrypted_passwords,
+                        &sfs,
+                        &new_home);
+        if (r < 0)
+                return r;
+
+        r = home_extend_embedded_identity(new_home, h, &setup);
+        if (r < 0)
+                return r;
+
+        setup.root_fd = safe_close(setup.root_fd);
+
+        r = home_move_mount(user_record_user_name_and_realm(h), hd);
+        if (r < 0)
+                return r;
+
+        setup.undo_mount = false;
+
+        loop_device_relinquish(setup.loop);
+
+        r = dm_deferred_remove(setup.dm_name);
+        if (r < 0)
+                log_warning_errno(r, "Failed to relinquish dm device, ignoring: %m");
+
+        setup.undo_dm = false;
+
+        log_info("Everything completed.");
+
+        print_size_summary(host_size, encrypted_size, &sfs);
+
+        *ret_home = TAKE_PTR(new_home);
+        return 1;
+}
+
+int home_deactivate_luks(UserRecord *h) {
+        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
+        int r;
+
+        /* Note that the DM device and loopback device are set to auto-detach, hence strictly speaking we
+         * don't have to explicitly have to detach them. However, we do that nonetheless (in case of the DM
+         * device), to avoid races: by explicitly detaching them we know when the detaching is complete. We
+         * don't bother about the loopback device because unlike the DM device it doesn't have a fixed
+         * name. */
+
+        r = make_dm_names(h->user_name, &dm_name, &dm_node);
+        if (r < 0)
+                return r;
+
+        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;
+        } else if (r < 0)
+                return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", dm_name);
+
+        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);
+        else if (r < 0)
+                return log_info_errno(r, "LUKS device %s couldn't be deactivated: %m", dm_node);
+
+        log_info("LUKS device detaching completed.");
+        return true;
+}
+
+static int run_mkfs(
+                const char *node,
+                const char *fstype,
+                const char *label,
+                sd_id128_t uuid,
+                bool discard) {
+
+        int r;
+
+        assert(node);
+        assert(fstype);
+        assert(label);
+
+        r = mkfs_exists(fstype);
+        if (r < 0)
+                return log_error_errno(r, "Failed to check if mkfs for file system %s exists: %m", fstype);
+        if (r == 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "Nt mkfs for file system %s installed.", fstype);
+
+        r = safe_fork("(mkfs)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR, NULL);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                const char *mkfs;
+                char suuid[37];
+
+                /* Child */
+
+                mkfs = strjoina("mkfs.", fstype);
+                id128_to_uuid_string(uuid, suuid);
+
+                if (streq(fstype, "ext4"))
+                        execlp(mkfs, mkfs,
+                               "-L", label,
+                               "-U", suuid,
+                               "-I", "256",
+                               "-O", "has_journal",
+                               "-m", "0",
+                               "-E", discard ? "lazy_itable_init=1,discard" : "lazy_itable_init=1,nodiscard",
+                               node, NULL);
+                else if (streq(fstype, "btrfs")) {
+                        if (discard)
+                                execlp(mkfs, mkfs, "-L", label, "-U", suuid, node, NULL);
+                        else
+                                execlp(mkfs, mkfs, "-L", label, "-U", suuid, "--nodiscard", node, NULL);
+                } else if (streq(fstype, "xfs")) {
+                        const char *j;
+
+                        j = strjoina("uuid=", suuid);
+                        if (discard)
+                                execlp(mkfs, mkfs, "-L", label, "-m", j, "-m", "reflink=1", node, NULL);
+                        else
+                                execlp(mkfs, mkfs, "-L", label, "-m", j, "-m", "reflink=1", "-K", node, NULL);
+                } else {
+                        log_error("Cannot make file system: %s", fstype);
+                        _exit(EXIT_FAILURE);
+                }
+
+                log_error_errno(errno, "Failed to execute %s: %m", mkfs);
+                _exit(EXIT_FAILURE);
+        }
+
+        return 0;
+}
+
+static struct crypt_pbkdf_type* build_good_pbkdf(struct crypt_pbkdf_type *buffer, UserRecord *hr) {
+        assert(buffer);
+        assert(hr);
+
+        *buffer = (struct crypt_pbkdf_type) {
+                .hash = user_record_luks_pbkdf_hash_algorithm(hr),
+                .type = user_record_luks_pbkdf_type(hr),
+                .time_ms = user_record_luks_pbkdf_time_cost_usec(hr) / USEC_PER_MSEC,
+                .max_memory_kb = user_record_luks_pbkdf_memory_cost(hr) / 1024,
+                .parallel_threads = user_record_luks_pbkdf_parallel_threads(hr),
+        };
+
+        return buffer;
+}
+
+static struct crypt_pbkdf_type* build_minimal_pbkdf(struct crypt_pbkdf_type *buffer, UserRecord *hr) {
+        assert(buffer);
+        assert(hr);
+
+        /* For PKCS#11 derived keys (which are generated randomly and are of high quality already) we use a
+         * minimal PBKDF */
+        *buffer = (struct crypt_pbkdf_type) {
+                .hash = user_record_luks_pbkdf_hash_algorithm(hr),
+                .type = CRYPT_KDF_PBKDF2,
+                .iterations = 1,
+                .time_ms = 1,
+        };
+
+        return buffer;
+}
+
+static int luks_format(
+                const char *node,
+                const char *dm_name,
+                sd_id128_t uuid,
+                const char *label,
+                char **pkcs11_decrypted_passwords,
+                char **effective_passwords,
+                bool discard,
+                UserRecord *hr,
+                struct crypt_device **ret) {
+
+        _cleanup_(user_record_unrefp) UserRecord *reduced = NULL;
+        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_(erase_and_freep) void *volume_key = NULL;
+        struct crypt_pbkdf_type good_pbkdf, minimal_pbkdf;
+        _cleanup_free_ char *text = NULL;
+        size_t volume_key_size;
+        char suuid[37], **pp;
+        int slot = 0, r;
+
+        assert(node);
+        assert(dm_name);
+        assert(hr);
+        assert(ret);
+
+        r = crypt_init(&cd, node);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate libcryptsetup context: %m");
+
+        crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
+
+        /* Normally we'd, just leave volume key generation to libcryptsetup. However, we can't, since we
+         * can't extract the volume key from the library again, but we need it in order to encrypt the JSON
+         * record. Hence, let's generate it on our own, so that we can keep track of it. */
+
+        volume_key_size = user_record_luks_volume_key_size(hr);
+        volume_key = malloc(volume_key_size);
+        if (!volume_key)
+                return log_oom();
+
+        r = genuine_random_bytes(volume_key, volume_key_size, RANDOM_BLOCK);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate volume key: %m");
+
+#if HAVE_CRYPT_SET_METADATA_SIZE
+        /* Increase the metadata space to 4M, the largest LUKS2 supports */
+        r = crypt_set_metadata_size(cd, 4096U*1024U, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to change LUKS2 metadata size: %m");
+#endif
+
+        build_good_pbkdf(&good_pbkdf, hr);
+        build_minimal_pbkdf(&minimal_pbkdf, hr);
+
+        r = crypt_format(cd,
+                         CRYPT_LUKS2,
+                         user_record_luks_cipher(hr),
+                         user_record_luks_cipher_mode(hr),
+                         id128_to_uuid_string(uuid, suuid),
+                         volume_key,
+                         volume_key_size,
+                         &(struct crypt_params_luks2) {
+                                 .label = label,
+                                 .subsystem = "systemd-home",
+                                 .sector_size = 512U,
+                                 .pbkdf = &good_pbkdf,
+                         });
+        if (r < 0)
+                return log_error_errno(r, "Failed to format LUKS image: %m");
+
+        log_info("LUKS formatting completed.");
+
+        STRV_FOREACH(pp, effective_passwords) {
+
+                if (strv_contains(pkcs11_decrypted_passwords, *pp)) {
+                        log_debug("Using minimal PBKDF for slot %i", slot);
+                        r = crypt_set_pbkdf_type(cd, &minimal_pbkdf);
+                } else {
+                        log_debug("Using good PBKDF for slot %i", slot);
+                        r = crypt_set_pbkdf_type(cd, &good_pbkdf);
+                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to tweak PBKDF for slot %i: %m", slot);
+
+                r = crypt_keyslot_add_by_volume_key(
+                                cd,
+                                slot,
+                                volume_key,
+                                volume_key_size,
+                                *pp,
+                                strlen(*pp));
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set up LUKS password for slot %i: %m", slot);
+
+                log_info("Writing password to LUKS keyslot %i completed.", slot);
+                slot++;
+        }
+
+        r = crypt_activate_by_volume_key(
+                        cd,
+                        dm_name,
+                        volume_key,
+                        volume_key_size,
+                        discard ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to activate LUKS superblock: %m");
+
+        log_info("LUKS activation by volume key succeeded.");
+
+        r = user_record_clone(hr, USER_RECORD_EXTRACT_EMBEDDED, &reduced);
+        if (r < 0)
+                return log_error_errno(r, "Failed to prepare home record for LUKS: %m");
+
+        r = format_luks_token_text(cd, reduced, volume_key, &text);
+        if (r < 0)
+                return r;
+
+        r = crypt_token_json_set(cd, CRYPT_ANY_TOKEN, text);
+        if (r < 0)
+                return log_error_errno(r, "Failed to set LUKS JSON token: %m");
+
+        log_info("Writing user record as LUKS token completed.");
+
+        if (ret)
+                *ret = TAKE_PTR(cd);
+
+        return 0;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct fdisk_context*, fdisk_unref_context);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct fdisk_partition*, fdisk_unref_partition);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct fdisk_parttype*, fdisk_unref_parttype);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct fdisk_table*, fdisk_unref_table);
+
+static int make_partition_table(
+                int fd,
+                const char *label,
+                sd_id128_t uuid,
+                uint64_t *ret_offset,
+                uint64_t *ret_size,
+                sd_id128_t *ret_disk_uuid) {
+
+        _cleanup_(fdisk_unref_partitionp) struct fdisk_partition *p = NULL, *q = NULL;
+        _cleanup_(fdisk_unref_parttypep) struct fdisk_parttype *t = NULL;
+        _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
+        _cleanup_free_ char *path = NULL, *disk_uuid_as_string = NULL;
+        uint64_t offset, size;
+        sd_id128_t disk_uuid;
+        char uuids[37];
+        int r;
+
+        assert(fd >= 0);
+        assert(label);
+        assert(ret_offset);
+        assert(ret_size);
+
+        t = fdisk_new_parttype();
+        if (!t)
+                return log_oom();
+
+        r = fdisk_parttype_set_typestr(t, "773f91ef-66d4-49b5-bd83-d683bf40ad16");
+        if (r < 0)
+                return log_error_errno(r, "Failed to initialize partition type: %m");
+
+        c = fdisk_new_context();
+        if (!c)
+                return log_oom();
+
+        if (asprintf(&path, "/proc/self/fd/%i", fd) < 0)
+                return log_oom();
+
+        r = fdisk_assign_device(c, path, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open device: %m");
+
+        r = fdisk_create_disklabel(c, "gpt");
+        if (r < 0)
+                return log_error_errno(r, "Failed to create gpt disk label: %m");
+
+        p = fdisk_new_partition();
+        if (!p)
+                return log_oom();
+
+        r = fdisk_partition_set_type(p, t);
+        if (r < 0)
+                return log_error_errno(r, "Failed to set partition type: %m");
+
+        r = fdisk_partition_start_follow_default(p, 1);
+        if (r < 0)
+                return log_error_errno(r, "Failed to place partition at beginning of space: %m");
+
+        r = fdisk_partition_partno_follow_default(p, 1);
+        if (r < 0)
+                return log_error_errno(r, "Failed to place partition at first free partition index: %m");
+
+        r = fdisk_partition_end_follow_default(p, 1);
+        if (r < 0)
+                return log_error_errno(r, "Failed to make partition cover all free space: %m");
+
+        r = fdisk_partition_set_name(p, label);
+        if (r < 0)
+                return log_error_errno(r, "Failed to set partition name: %m");
+
+        r = fdisk_partition_set_uuid(p, id128_to_uuid_string(uuid, uuids));
+        if (r < 0)
+                return log_error_errno(r, "Failed to set partition UUID: %m");
+
+        r = fdisk_add_partition(c, p, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add partition: %m");
+
+        r = fdisk_write_disklabel(c);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write disk label: %m");
+
+        r = fdisk_get_disklabel_id(c, &disk_uuid_as_string);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine disk label UUID: %m");
+
+        r = sd_id128_from_string(disk_uuid_as_string, &disk_uuid);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse disk label UUID: %m");
+
+        r = fdisk_get_partition(c, 0, &q);
+        if (r < 0)
+                return log_error_errno(r, "Failed to read created partition metadata: %m");
+
+        assert(fdisk_partition_has_start(q));
+        offset = fdisk_partition_get_start(q);
+        if (offset > UINT64_MAX / 512U)
+                return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Partition offset too large.");
+
+        assert(fdisk_partition_has_size(q));
+        size = fdisk_partition_get_size(q);
+        if (size > UINT64_MAX / 512U)
+                return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Partition size too large.");
+
+        *ret_offset = offset * 512U;
+        *ret_size = size * 512U;
+        *ret_disk_uuid = disk_uuid;
+
+        return 0;
+}
+
+static bool supported_fs_size(const char *fstype, uint64_t host_size) {
+        uint64_t m;
+
+        m = minimal_size_by_fs_name(fstype);
+        if (m == UINT64_MAX)
+                return false;
+
+        return host_size >= m;
+}
+
+static int wait_for_devlink(const char *path) {
+        _cleanup_close_ int inotify_fd = -1;
+        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
+         * 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. */
+
+        until = usec_add(now(CLOCK_MONOTONIC), 45 * USEC_PER_SEC);
+
+        for (;;) {
+                _cleanup_free_ char *dn = NULL;
+                usec_t w;
+
+                if (laccess(path, F_OK) < 0) {
+                        if (errno != ENOENT)
+                                return log_error_errno(errno, "Failed to determine whether %s exists: %m", path);
+                } else
+                        return 0; /* Found it */
+
+                if (inotify_fd < 0) {
+                        /* We need to wait for the device symlink to show up, let's create an inotify watch for it */
+                        inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
+                        if (inotify_fd < 0)
+                                return log_error_errno(errno, "Failed to allocate inotify fd: %m");
+                }
+
+                dn = dirname_malloc(path);
+                for (;;) {
+                        if (!dn)
+                                return log_oom();
+
+                        log_info("Watching %s", dn);
+
+                        if (inotify_add_watch(inotify_fd, dn, IN_CREATE|IN_MOVED_TO|IN_ONLYDIR|IN_DELETE_SELF|IN_MOVE_SELF) < 0) {
+                                if (errno != ENOENT)
+                                        return log_error_errno(errno, "Failed to add watch on %s: %m", dn);
+                        } else
+                                break;
+
+                        if (empty_or_root(dn))
+                                break;
+
+                        dn = dirname_malloc(dn);
+                }
+
+                w = now(CLOCK_MONOTONIC);
+                if (w >= until)
+                        return log_error_errno(SYNTHETIC_ERRNO(ETIMEDOUT), "Device link %s still hasn't shown up, giving up.", path);
+
+                r = fd_wait_for_event(inotify_fd, POLLIN, usec_sub_unsigned(until, w));
+                if (r < 0)
+                        return log_error_errno(r, "Failed to watch inotify: %m");
+
+                (void) flush_fd(inotify_fd);
+        }
+}
+
+static int calculate_disk_size(UserRecord *h, const char *parent_dir, uint64_t *ret) {
+        char buf[FORMAT_BYTES_MAX];
+        struct statfs sfs;
+        uint64_t m;
+
+        assert(h);
+        assert(parent_dir);
+        assert(ret);
+
+        if (h->disk_size != UINT64_MAX) {
+                *ret = DISK_SIZE_ROUND_DOWN(h->disk_size);
+                return 0;
+        }
+
+        if (statfs(parent_dir, &sfs) < 0)
+                return log_error_errno(errno, "statfs() on %s failed: %m", parent_dir);
+
+        m = sfs.f_bsize * sfs.f_bavail;
+
+        if (h->disk_size_relative == UINT64_MAX) {
+
+                if (m > UINT64_MAX / USER_DISK_SIZE_DEFAULT_PERCENT)
+                        return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "Disk size too large.");
+
+                *ret = DISK_SIZE_ROUND_DOWN(m * USER_DISK_SIZE_DEFAULT_PERCENT / 100);
+
+                log_info("Sizing home to %u%% of available disk space, which is %s.",
+                         USER_DISK_SIZE_DEFAULT_PERCENT,
+                         format_bytes(buf, sizeof(buf), *ret));
+        } else {
+                *ret = DISK_SIZE_ROUND_DOWN((uint64_t) ((double) m * (double) h->disk_size_relative / (double) UINT32_MAX));
+
+                log_info("Sizing home to %" PRIu64 ".%01" PRIu64 "%% of available disk space, which is %s.",
+                         (h->disk_size_relative * 100) / UINT32_MAX,
+                         ((h->disk_size_relative * 1000) / UINT32_MAX) % 10,
+                         format_bytes(buf, sizeof(buf), *ret));
+        }
+
+        if (*ret < USER_DISK_SIZE_MIN)
+                *ret = USER_DISK_SIZE_MIN;
+
+        return 0;
+}
+
+int home_create_luks(
+                UserRecord *h,
+                char **pkcs11_decrypted_passwords,
+                char **effective_passwords,
+                UserRecord **ret_home) {
+
+        _cleanup_free_ char *dm_name = NULL, *dm_node = NULL, *subdir = NULL, *disk_uuid_path = NULL, *temporary_image_path = NULL;
+        uint64_t host_size, encrypted_size, partition_offset, partition_size;
+        bool image_created = false, dm_activated = false, mounted = false;
+        _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
+        sd_id128_t partition_uuid, fs_uuid, luks_uuid, disk_uuid;
+        _cleanup_(loop_device_unrefp) LoopDevice *loop = NULL;
+        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_close_ int image_fd = -1, root_fd = -1;
+        const char *fstype, *ip;
+        struct statfs sfs;
+        int r;
+
+        assert(h);
+        assert(h->storage < 0 || h->storage == USER_LUKS);
+        assert(ret_home);
+
+        assert_se(ip = user_record_image_path(h));
+
+        fstype = user_record_file_system_type(h);
+        if (!supported_fstype(fstype))
+                return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "Unsupported file system type: %s", h->file_system_type);
+
+        if (sd_id128_is_null(h->partition_uuid)) {
+                r = sd_id128_randomize(&partition_uuid);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to acquire partition UUID: %m");
+        } else
+                partition_uuid = h->partition_uuid;
+
+        if (sd_id128_is_null(h->luks_uuid)) {
+                r = sd_id128_randomize(&luks_uuid);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to acquire LUKS UUID: %m");
+        } else
+                luks_uuid = h->luks_uuid;
+
+        if (sd_id128_is_null(h->file_system_uuid)) {
+                r = sd_id128_randomize(&fs_uuid);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to acquire file system UUID: %m");
+        } else
+                fs_uuid = h->file_system_uuid;
+
+        r = make_dm_names(h->user_name, &dm_name, &dm_node);
+        if (r < 0)
+                return r;
+
+        r = access(dm_node, F_OK);
+        if (r < 0) {
+                if (errno != ENOENT)
+                        return log_error_errno(errno, "Failed to determine whether %s exists: %m", dm_node);
+        } else
+                return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Device mapper device %s already exists, refusing.", dm_node);
+
+        if (path_startswith(ip, "/dev/")) {
+                _cleanup_free_ char *sysfs = NULL;
+                uint64_t block_device_size;
+                struct stat st;
+
+                /* Let's place the home directory on a real device, i.e. an USB stick or such */
+
+                image_fd = open(ip, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+                if (image_fd < 0)
+                        return log_error_errno(errno, "Failed to open device %s: %m", ip);
+
+                if (fstat(image_fd, &st) < 0)
+                        return log_error_errno(errno, "Failed to stat device %s: %m", ip);
+                if (!S_ISBLK(st.st_mode))
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Device is not a block device, refusing.");
+
+                if (asprintf(&sysfs, "/sys/dev/block/%u:%u/partition", major(st.st_rdev), minor(st.st_rdev)) < 0)
+                        return log_oom();
+                if (access(sysfs, F_OK) < 0) {
+                        if (errno != ENOENT)
+                                return log_error_errno(errno, "Failed to check whether %s exists: %m", sysfs);
+                } else
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Operating on partitions is currently not supported, sorry. Please specify a top-level block device.");
+
+                if (flock(image_fd, LOCK_EX) < 0) /* make sure udev doesn't read from it while we operate on the device */
+                        return log_error_errno(errno, "Failed to lock block device %s: %m", ip);
+
+                if (ioctl(image_fd, BLKGETSIZE64, &block_device_size) < 0)
+                        return log_error_errno(errno, "Failed to read block device size: %m");
+
+                if (h->disk_size == UINT64_MAX) {
+
+                        /* If a relative disk size is requested, apply it relative to the block device size */
+                        if (h->disk_size_relative < UINT32_MAX)
+                                host_size = CLAMP(DISK_SIZE_ROUND_DOWN(block_device_size * h->disk_size_relative / UINT32_MAX),
+                                                  USER_DISK_SIZE_MIN, USER_DISK_SIZE_MAX);
+                        else
+                                host_size = block_device_size; /* Otherwise, take the full device */
+
+                } else if (h->disk_size > block_device_size)
+                        return log_error_errno(SYNTHETIC_ERRNO(EMSGSIZE), "Selected disk size larger than backing block device, refusing.");
+                else
+                        host_size = DISK_SIZE_ROUND_DOWN(h->disk_size);
+
+                if (!supported_fs_size(fstype, host_size))
+                        return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Selected file system size too small for %s.", h->file_system_type);
+
+                /* After creation we should reference this partition by its UUID instead of the block
+                 * device. That's preferable since the user might have specified a device node such as
+                 * /dev/sdb to us, which might look very different when replugged. */
+                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 (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");
+                        else
+                                log_info("Full device discard completed.");
+                }
+        } else {
+                _cleanup_free_ char *parent = NULL;
+
+                parent = dirname_malloc(ip);
+                if (!parent)
+                        return log_oom();
+
+                r = mkdir_p(parent, 0755);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to create parent directory %s: %m", parent);
+
+                r = calculate_disk_size(h, parent, &host_size);
+                if (r < 0)
+                        return r;
+
+                if (!supported_fs_size(fstype, host_size))
+                        return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Selected file system size too small for %s.", h->file_system_type);
+
+                r = tempfn_random(ip, "homework", &temporary_image_path);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to derive temporary file name for %s: %m", ip);
+
+                image_fd = open(temporary_image_path, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
+                if (image_fd < 0)
+                        return log_error_errno(errno, "Failed to create home image %s: %m", temporary_image_path);
+
+                image_created = true;
+
+                r = chattr_fd(image_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
+                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);
+                        goto fail;
+                }
+
+                log_info("Allocating image file completed.");
+        }
+
+        r = make_partition_table(
+                        image_fd,
+                        user_record_user_name_and_realm(h),
+                        partition_uuid,
+                        &partition_offset,
+                        &partition_size,
+                        &disk_uuid);
+        if (r < 0)
+                goto fail;
+
+        log_info("Writing of partition table completed.");
+
+        r = loop_device_make(image_fd, O_RDWR, partition_offset, partition_size, 0, &loop);
+        if (r < 0) {
+                if (r == -ENOENT) { /* this means /dev/loop-control doesn't exist, i.e. we are in a container
+                                     * or similar and loopback bock devices are not available, return a
+                                     * recognizable error in this case. */
+                        log_error_errno(r, "Loopback block device support is not available on this system.");
+                        r = -ENOLINK;
+                        goto fail;
+                }
+
+                log_error_errno(r, "Failed to set up loopback device for %s: %m", temporary_image_path);
+                goto fail;
+        }
+
+        r = loop_device_flock(loop, LOCK_EX); /* make sure udev won't read before we are done */
+        if (r < 0) {
+                log_error_errno(r, "Failed to take lock on loop device: %m");
+                goto fail;
+        }
+
+        log_info("Setting up loopback device %s completed.", loop->node ?: ip);
+
+        r = luks_format(loop->node,
+                        dm_name,
+                        luks_uuid,
+                        user_record_user_name_and_realm(h),
+                        pkcs11_decrypted_passwords,
+                        effective_passwords,
+                        user_record_luks_discard(h),
+                        h,
+                        &cd);
+        if (r < 0)
+                goto fail;
+
+        dm_activated = true;
+
+        r = block_get_size_by_path(dm_node, &encrypted_size);
+        if (r < 0) {
+                log_error_errno(r, "Failed to get encrypted block device size: %m");
+                goto fail;
+        }
+
+        log_info("Setting up LUKS device %s completed.", dm_node);
+
+        r = run_mkfs(dm_node, fstype, user_record_user_name_and_realm(h), fs_uuid, user_record_luks_discard(h));
+        if (r < 0)
+                goto fail;
+
+        log_info("Formatting file system completed.");
+
+        r = home_unshare_and_mount(dm_node, fstype, user_record_luks_discard(h));
+        if (r < 0)
+                goto fail;
+
+        mounted = true;
+
+        subdir = path_join("/run/systemd/user-home-mount/", user_record_user_name_and_realm(h));
+        if (!subdir) {
+                r = log_oom();
+                goto fail;
+        }
+
+        if (mkdir(subdir, 0700) < 0) {
+                r = log_error_errno(errno, "Failed to create user directory in mounted image file: %m");
+                goto fail;
+        }
+
+        root_fd = open(subdir, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+        if (root_fd < 0) {
+                r = log_error_errno(errno, "Failed to open user directory in mounted image file: %m");
+                goto fail;
+        }
+
+        r = home_populate(h, root_fd);
+        if (r < 0)
+                goto fail;
+
+        r = home_sync_and_statfs(root_fd, &sfs);
+        if (r < 0)
+                goto fail;
+
+        r = user_record_clone(h, USER_RECORD_LOAD_MASK_SECRET|USER_RECORD_LOG, &new_home);
+        if (r < 0) {
+                log_error_errno(r, "Failed to clone record: %m");
+                goto fail;
+        }
+
+        r = user_record_add_binding(
+                        new_home,
+                        USER_LUKS,
+                        disk_uuid_path ?: ip,
+                        partition_uuid,
+                        luks_uuid,
+                        fs_uuid,
+                        crypt_get_cipher(cd),
+                        crypt_get_cipher_mode(cd),
+                        luks_volume_key_size_convert(cd),
+                        fstype,
+                        NULL,
+                        h->uid,
+                        (gid_t) h->uid);
+        if (r < 0) {
+                log_error_errno(r, "Failed to add binding to record: %m");
+                goto fail;
+        }
+
+        root_fd = safe_close(root_fd);
+
+        r = umount_verbose("/run/systemd/user-home-mount");
+        if (r < 0)
+                goto fail;
+
+        mounted = false;
+
+        r = crypt_deactivate(cd, dm_name);
+        if (r < 0) {
+                log_error_errno(r, "Failed to deactivate LUKS device: %m");
+                goto fail;
+        }
+
+        dm_activated = false;
+
+        loop = loop_device_unref(loop);
+
+        if (disk_uuid_path)
+                (void) ioctl(image_fd, BLKRRPART, 0);
+
+        /* Let's close the image fd now. If we are operating on a real block device this will release the BSD
+         * lock that ensures udev doesn't interfere with what we are doing */
+        image_fd = safe_close(image_fd);
+
+        if (temporary_image_path) {
+                if (rename(temporary_image_path, ip) < 0) {
+                        log_error_errno(errno, "Failed to rename image file: %m");
+                        goto fail;
+                }
+
+                log_info("Moved image file into place.");
+        }
+
+        if (disk_uuid_path)
+                (void) wait_for_devlink(disk_uuid_path);
+
+        log_info("Everything completed.");
+
+        print_size_summary(host_size, encrypted_size, &sfs);
+
+        *ret_home = TAKE_PTR(new_home);
+        return 0;
+
+fail:
+        /* Let's close all files before we unmount the file system, to avoid EBUSY */
+        root_fd = safe_close(root_fd);
+
+        if (mounted)
+                (void) umount_verbose("/run/systemd/user-home-mount");
+
+        if (dm_activated)
+                (void) crypt_deactivate(cd, dm_name);
+
+        loop = loop_device_unref(loop);
+
+        if (image_created)
+                (void) unlink(temporary_image_path);
+
+        return r;
+}
+
+int home_validate_update_luks(UserRecord *h, HomeSetup *setup) {
+        _cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
+        int r;
+
+        assert(h);
+        assert(setup);
+
+        r = make_dm_names(h->user_name, &dm_name, &dm_node);
+        if (r < 0)
+                return r;
+
+        r = access(dm_node, F_OK);
+        if (r < 0 && errno != ENOENT)
+                return log_error_errno(errno, "Failed to determine whether %s exists: %m", dm_node);
+
+        free_and_replace(setup->dm_name, dm_name);
+        free_and_replace(setup->dm_node, dm_node);
+
+        return r >= 0;
+}
+
+enum {
+        CAN_RESIZE_ONLINE,
+        CAN_RESIZE_OFFLINE,
+};
+
+static int can_resize_fs(int fd, uint64_t old_size, uint64_t new_size) {
+        struct statfs sfs;
+
+        assert(fd >= 0);
+
+        /* Filter out bogus requests early */
+        if (old_size == 0 || old_size == UINT64_MAX ||
+            new_size == 0 || new_size == UINT64_MAX)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid resize parameters.");
+
+        if ((old_size & 511) != 0 || (new_size & 511) != 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Resize parameters not multiple of 512.");
+
+        if (fstatfs(fd, &sfs) < 0)
+                return log_error_errno(errno, "Failed to fstatfs() file system: %m");
+
+        if (is_fs_type(&sfs, BTRFS_SUPER_MAGIC)) {
+
+                if (new_size < BTRFS_MINIMAL_SIZE)
+                        return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "New file system size too small for btrfs (needs to be 256M at least.");
+
+                /* btrfs can grow and shrink online */
+
+        } else if (is_fs_type(&sfs, XFS_SB_MAGIC)) {
+
+                if (new_size < XFS_MINIMAL_SIZE)
+                        return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "New file system size too small for xfs (needs to be 14M at least).");
+
+                /* XFS can grow, but not shrink */
+                if (new_size < old_size)
+                        return log_error_errno(SYNTHETIC_ERRNO(EMSGSIZE), "Shrinking this type of file system is not supported.");
+
+        } else if (is_fs_type(&sfs, EXT4_SUPER_MAGIC)) {
+
+                if (new_size < EXT4_MINIMAL_SIZE)
+                        return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "New file system size too small for ext4 (needs to be 1M at least).");
+
+                /* ext4 can grow online, and shrink offline */
+                if (new_size < old_size)
+                        return CAN_RESIZE_OFFLINE;
+
+        } else
+                return log_error_errno(SYNTHETIC_ERRNO(ESOCKTNOSUPPORT), "Resizing this type of file system is not supported.");
+
+        return CAN_RESIZE_ONLINE;
+}
+
+static int ext4_offline_resize_fs(HomeSetup *setup, uint64_t new_size, bool discard) {
+        _cleanup_free_ char *size_str = NULL;
+        bool re_open = false, re_mount = false;
+        pid_t resize_pid, fsck_pid;
+        int r, exit_status;
+
+        assert(setup);
+        assert(setup->dm_node);
+
+        /* First, unmount the file system */
+        if (setup->root_fd >= 0) {
+                setup->root_fd = safe_close(setup->root_fd);
+                re_open = true;
+        }
+
+        if (setup->undo_mount) {
+                r = umount_verbose("/run/systemd/user-home-mount");
+                if (r < 0)
+                        return r;
+
+                setup->undo_mount = false;
+                re_mount = true;
+        }
+
+        log_info("Temporarary unmounting of file system completed.");
+
+        /* resize2fs requires that the file system is force checked first, do so. */
+        r = safe_fork("(e2fsck)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_STDOUT_TO_STDERR, &fsck_pid);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                /* Child */
+                execlp("e2fsck" ,"e2fsck", "-fp", setup->dm_node, NULL);
+                log_error_errno(errno, "Failed to execute e2fsck: %m");
+                _exit(EXIT_FAILURE);
+        }
+
+        exit_status = wait_for_terminate_and_check("e2fsck", fsck_pid, WAIT_LOG_ABNORMAL);
+        if (exit_status < 0)
+                return exit_status;
+        if ((exit_status & ~FSCK_ERROR_CORRECTED) != 0) {
+                log_warning("e2fsck failed with exit status %i.", exit_status);
+
+                if ((exit_status & (FSCK_SYSTEM_SHOULD_REBOOT|FSCK_ERRORS_LEFT_UNCORRECTED)) != 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "File system is corrupted, refusing.");
+
+                log_warning("Ignoring fsck error.");
+        }
+
+        log_info("Forced file system check completed.");
+
+        /* We use 512 sectors here, because resize2fs doesn't do byte sizes */
+        if (asprintf(&size_str, "%" PRIu64 "s", new_size / 512) < 0)
+                return log_oom();
+
+        /* Resize the thing */
+        r = safe_fork("(e2resize)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR, &resize_pid);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                /* Child */
+                execlp("resize2fs" ,"resize2fs", setup->dm_node, size_str, NULL);
+                log_error_errno(errno, "Failed to execute resize2fs: %m");
+                _exit(EXIT_FAILURE);
+        }
+
+        log_info("Offline file system resize completed.");
+
+        /* Re-establish mounts and reopen the directory */
+        if (re_mount) {
+                r = home_mount_node(setup->dm_node, "ext4", discard);
+                if (r < 0)
+                        return r;
+
+                setup->undo_mount = true;
+        }
+
+        if (re_open) {
+                setup->root_fd = open("/run/systemd/user-home-mount", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+                if (setup->root_fd < 0)
+                        return log_error_errno(errno, "Failed to reopen file system: %m");
+        }
+
+        log_info("File system mounted again.");
+
+        return 0;
+}
+
+static int prepare_resize_partition(
+                int fd,
+                uint64_t partition_offset,
+                uint64_t old_partition_size,
+                uint64_t new_partition_size,
+                sd_id128_t *ret_disk_uuid,
+                struct fdisk_table **ret_table) {
+
+        _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
+        _cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL;
+        _cleanup_free_ char *path = NULL, *disk_uuid_as_string = NULL;
+        size_t n_partitions, i;
+        sd_id128_t disk_uuid;
+        bool found = false;
+        int r;
+
+        assert(fd >= 0);
+        assert(ret_disk_uuid);
+        assert(ret_table);
+
+        assert((partition_offset & 511) == 0);
+        assert((old_partition_size & 511) == 0);
+        assert((new_partition_size & 511) == 0);
+        assert(UINT64_MAX - old_partition_size >= partition_offset);
+        assert(UINT64_MAX - new_partition_size >= partition_offset);
+
+        if (partition_offset == 0) {
+                /* If the offset is at the beginning we assume no partition table, let's exit early. */
+                log_debug("Not rewriting partition table, operating on naked device.");
+                *ret_disk_uuid = SD_ID128_NULL;
+                *ret_table = NULL;
+                return 0;
+        }
+
+        c = fdisk_new_context();
+        if (!c)
+                return log_oom();
+
+        if (asprintf(&path, "/proc/self/fd/%i", fd) < 0)
+                return log_oom();
+
+        r = fdisk_assign_device(c, path, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open device: %m");
+
+        if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT))
+                return log_error_errno(SYNTHETIC_ERRNO(ENOMEDIUM), "Disk has no GPT partition table.");
+
+        r = fdisk_get_disklabel_id(c, &disk_uuid_as_string);
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire disk UUID: %m");
+
+        r = sd_id128_from_string(disk_uuid_as_string, &disk_uuid);
+        if (r < 0)
+                return log_error_errno(r, "Failed parse disk UUID: %m");
+
+        r = fdisk_get_partitions(c, &t);
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire partition table: %m");
+
+        n_partitions = fdisk_table_get_nents(t);
+        for (i = 0; i < n_partitions; i++)  {
+                struct fdisk_partition *p;
+
+                p = fdisk_table_get_partition(t, i);
+                if (!p)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read partition metadata: %m");
+
+                if (fdisk_partition_is_used(p) <= 0)
+                        continue;
+                if (fdisk_partition_has_start(p) <= 0 || fdisk_partition_has_size(p) <= 0 || fdisk_partition_has_end(p) <= 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found partition without a size.");
+
+                if (fdisk_partition_get_start(p) == partition_offset / 512U &&
+                    fdisk_partition_get_size(p) == old_partition_size / 512U) {
+
+                        if (found)
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ), "Partition found twice, refusing.");
+
+                        /* Found our partition, now patch it */
+                        r = fdisk_partition_size_explicit(p, 1);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to enable explicit partition size: %m");
+
+                        r = fdisk_partition_set_size(p, new_partition_size / 512U);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to change partition size: %m");
+
+                        found = true;
+                        continue;
+
+                } else {
+                        if (fdisk_partition_get_start(p) < partition_offset + new_partition_size / 512U &&
+                            fdisk_partition_get_end(p) >= partition_offset / 512)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Can't extend, conflicting partition found.");
+                }
+        }
+
+        if (!found)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "Failed to find matching partition to resize.");
+
+        *ret_table = TAKE_PTR(t);
+        *ret_disk_uuid = disk_uuid;
+
+        return 1;
+}
+
+static int ask_cb(struct fdisk_context *c, struct fdisk_ask *ask, void *userdata) {
+        char *result;
+
+        assert(c);
+
+        switch (fdisk_ask_get_type(ask)) {
+
+        case FDISK_ASKTYPE_STRING:
+                result = new(char, 37);
+                if (!result)
+                        return log_oom();
+
+                fdisk_ask_string_set_result(ask, id128_to_uuid_string(*(sd_id128_t*) userdata, result));
+                break;
+
+        default:
+                log_debug("Unexpected question from libfdisk, ignoring.");
+        }
+
+        return 0;
+}
+
+static int apply_resize_partition(int fd, sd_id128_t disk_uuids, struct fdisk_table *t) {
+        _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
+        _cleanup_free_ void *two_zero_lbas = NULL;
+        _cleanup_free_ char *path = NULL;
+        ssize_t n;
+        int r;
+
+        assert(fd >= 0);
+
+        if (!t) /* no partition table to apply, exit early */
+                return 0;
+
+        two_zero_lbas = malloc0(1024U);
+        if (!two_zero_lbas)
+                return log_oom();
+
+        /* libfdisk appears to get confused by the existing PMBR. Let's explicitly flush it out. */
+        n = pwrite(fd, two_zero_lbas, 1024U, 0);
+        if (n < 0)
+                return log_error_errno(errno, "Failed to wipe partition table: %m");
+        if (n != 1024)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write while whiping partition table.");
+
+        c = fdisk_new_context();
+        if (!c)
+                return log_oom();
+
+        if (asprintf(&path, "/proc/self/fd/%i", fd) < 0)
+                return log_oom();
+
+        r = fdisk_assign_device(c, path, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open device: %m");
+
+        r = fdisk_create_disklabel(c, "gpt");
+        if (r < 0)
+                return log_error_errno(r, "Failed to create GPT disk label: %m");
+
+        r = fdisk_apply_table(c, t);
+        if (r < 0)
+                return log_error_errno(r, "Failed to apply partition table: %m");
+
+        r = fdisk_set_ask(c, ask_cb, &disk_uuids);
+        if (r < 0)
+                return log_error_errno(r, "Failed to set libfdisk query function: %m");
+
+        r = fdisk_set_disklabel_id(c);
+        if (r < 0)
+                return log_error_errno(r, "Failed to change disklabel ID: %m");
+
+        r = fdisk_write_disklabel(c);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write disk label: %m");
+
+        return 1;
+}
+
+int home_resize_luks(
+                UserRecord *h,
+                bool already_activated,
+                char ***pkcs11_decrypted_passwords,
+                HomeSetup *setup,
+                UserRecord **ret_home) {
+
+        char buffer1[FORMAT_BYTES_MAX], buffer2[FORMAT_BYTES_MAX], buffer3[FORMAT_BYTES_MAX],
+                buffer4[FORMAT_BYTES_MAX], buffer5[FORMAT_BYTES_MAX], buffer6[FORMAT_BYTES_MAX];
+        uint64_t old_image_size, new_image_size, old_fs_size, new_fs_size, crypto_offset, new_partition_size;
+        _cleanup_(user_record_unrefp) UserRecord *header_home = NULL, *embedded_home = NULL, *new_home = NULL;
+        _cleanup_(fdisk_unref_tablep) struct fdisk_table *table = NULL;
+        _cleanup_free_ char *whole_disk = NULL;
+        _cleanup_close_ int image_fd = -1;
+        sd_id128_t disk_uuid;
+        const char *ip, *ipo;
+        struct statfs sfs;
+        struct stat st;
+        int r, resize_type;
+
+        assert(h);
+        assert(user_record_storage(h) == USER_LUKS);
+        assert(setup);
+        assert(ret_home);
+
+        assert_se(ipo = user_record_image_path(h));
+        ip = strdupa(ipo); /* copy out since original might change later in home record object */
+
+        image_fd = open(ip, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+        if (image_fd < 0)
+                return log_error_errno(errno, "Failed to open image file %s: %m", ip);
+
+        if (fstat(image_fd, &st) < 0)
+                return log_error_errno(errno, "Failed to stat image file %s: %m", ip);
+        if (S_ISBLK(st.st_mode)) {
+                dev_t parent;
+
+                r = block_get_whole_disk(st.st_rdev, &parent);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to acquire whole block device for %s: %m", ip);
+                if (r > 0) {
+                        /* If we shall resize a file system on a partition device, then let's figure out the
+                         * whole disk device and operate on that instead, since we need to rewrite the
+                         * partition table to resize the partition. */
+
+                        log_info("Operating on partition device %s, using parent device.", ip);
+
+                        r = device_path_make_major_minor(st.st_mode, parent, &whole_disk);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to derive whole disk path for %s: %m", ip);
+
+                        safe_close(image_fd);
+
+                        image_fd = open(whole_disk, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+                        if (image_fd < 0)
+                                return log_error_errno(errno, "Failed to open whole block device %s: %m", whole_disk);
+
+                        if (fstat(image_fd, &st) < 0)
+                                return log_error_errno(errno, "Failed to stat whole block device %s: %m", whole_disk);
+                        if (!S_ISBLK(st.st_mode))
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Whole block device %s is not actually a block device, refusing.", whole_disk);
+                } else
+                        log_info("Operating on whole block device %s.", ip);
+
+                if (ioctl(image_fd, BLKGETSIZE64, &old_image_size) < 0)
+                        return log_error_errno(errno, "Failed to determine size of original block device: %m");
+
+                if (flock(image_fd, LOCK_EX) < 0) /* make sure udev doesn't read from it while we operate on the device */
+                        return log_error_errno(errno, "Failed to lock block device %s: %m", ip);
+
+                new_image_size = old_image_size; /* we can't resize physical block devices */
+        } else {
+                r = stat_verify_regular(&st);
+                if (r < 0)
+                        return log_error_errno(r, "Image file %s is not a block device nor regular: %m", ip);
+
+                old_image_size = st.st_size;
+
+                /* Note an asymetry here: when we operate on loopback files the specified disk size we get we
+                 * apply onto the loopback file as a whole. When we operate on block devices we instead apply
+                 * to the partition itself only. */
+
+                new_image_size = DISK_SIZE_ROUND_DOWN(h->disk_size);
+                if (new_image_size == old_image_size) {
+                        log_info("Image size already matching, skipping operation.");
+                        return 0;
+                }
+        }
+
+        r = home_prepare_luks(h, already_activated, whole_disk, pkcs11_decrypted_passwords, 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);
+        if (r < 0)
+                return r;
+
+        log_info("offset = %" PRIu64 ", size = %" PRIu64 ", image = %" PRIu64, setup->partition_offset, setup->partition_size, old_image_size);
+
+        if ((UINT64_MAX - setup->partition_offset) < setup->partition_size ||
+            setup->partition_offset + setup->partition_size > old_image_size)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Old partition doesn't fit in backing storage, refusing.");
+
+        if (S_ISREG(st.st_mode)) {
+                uint64_t partition_table_extra;
+
+                partition_table_extra = old_image_size - setup->partition_size;
+                if (new_image_size <= partition_table_extra)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "New size smaller than partition table metadata.");
+
+                new_partition_size = new_image_size - partition_table_extra;
+        } else {
+                assert(S_ISBLK(st.st_mode));
+
+                new_partition_size = DISK_SIZE_ROUND_DOWN(h->disk_size);
+                if (new_partition_size == setup->partition_size) {
+                        log_info("Partition size already matching, skipping operation.");
+                        return 0;
+                }
+        }
+
+        if ((UINT64_MAX - setup->partition_offset) < new_partition_size ||
+            setup->partition_offset + new_partition_size > new_image_size)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "New partition doesn't fit into backing storage, refusing.");
+
+        crypto_offset = crypt_get_data_offset(setup->crypt_device);
+        if (setup->partition_size / 512U <= crypto_offset)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Weird, old crypto payload offset doesn't actually fit in partition size?");
+        if (new_partition_size / 512U <= crypto_offset)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "New size smaller than crypto payload offset?");
+
+        old_fs_size = (setup->partition_size / 512U - crypto_offset) * 512U;
+        new_fs_size = (new_partition_size / 512U - crypto_offset) * 512U;
+
+        /* Before we start doing anything, let's figure out if we actually can */
+        resize_type = can_resize_fs(setup->root_fd, old_fs_size, new_fs_size);
+        if (resize_type < 0)
+                return resize_type;
+        if (resize_type == CAN_RESIZE_OFFLINE && already_activated)
+                return log_error_errno(SYNTHETIC_ERRNO(ETXTBSY), "File systems of this type can only be resized offline, but is currently online.");
+
+        log_info("Ready to resize image size %s → %s, partition size %s → %s, file system size %s → %s.",
+                 format_bytes(buffer1, sizeof(buffer1), old_image_size),
+                 format_bytes(buffer2, sizeof(buffer2), new_image_size),
+                 format_bytes(buffer3, sizeof(buffer3), setup->partition_size),
+                 format_bytes(buffer4, sizeof(buffer4), new_partition_size),
+                 format_bytes(buffer5, sizeof(buffer5), old_fs_size),
+                 format_bytes(buffer6, sizeof(buffer6), new_fs_size));
+
+        r = prepare_resize_partition(
+                        image_fd,
+                        setup->partition_offset,
+                        setup->partition_size,
+                        new_partition_size,
+                        &disk_uuid,
+                        &table);
+        if (r < 0)
+                return r;
+
+        if (new_fs_size > old_fs_size) {
+
+                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);
+                        }
+
+                        log_info("Growing of image file completed.");
+                }
+
+                /* Make sure loopback device sees the new bigger size */
+                r = loop_device_refresh_size(setup->loop, UINT64_MAX, new_partition_size);
+                if (r == -ENOTTY)
+                        log_debug_errno(r, "Device is not a loopback device, not refreshing size.");
+                else if (r < 0)
+                        return log_error_errno(r, "Failed to refresh loopback device size: %m");
+                else
+                        log_info("Refreshing loop device size completed.");
+
+                r = apply_resize_partition(image_fd, disk_uuid, table);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        log_info("Growing of partition completed.");
+
+                if (ioctl(image_fd, BLKRRPART, 0) < 0)
+                        log_debug_errno(errno, "BLKRRPART failed on block device, ignoring: %m");
+
+                /* Tell LUKS about the new bigger size too */
+                r = crypt_resize(setup->crypt_device, setup->dm_name, new_fs_size / 512U);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to grow LUKS device: %m");
+
+                log_info("LUKS device growing completed.");
+        } else {
+                r = home_store_embedded_identity(new_home, setup->root_fd, h->uid, embedded_home);
+                if (r < 0)
+                        return r;
+
+                if (S_ISREG(st.st_mode)) {
+                        if (user_record_luks_discard(h))
+                                /* Before we shrink, let's trim the file system, so that we need less space on disk during the shrinking */
+                                (void) run_fitrim(setup->root_fd);
+                        else {
+                                /* If discard is off, let's ensure all backing blocks are allocated, so that our resize operation doesn't fail half-way */
+                                r = run_fallocate(image_fd, &st);
+                                if (r < 0)
+                                        return r;
+                        }
+                }
+        }
+
+        /* Now resize the file system */
+        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));
+        if (r < 0)
+                return log_error_errno(r, "Failed to resize file system: %m");
+
+        log_info("File system resizing completed.");
+
+        /* Immediately sync afterwards */
+        r = home_sync_and_statfs(setup->root_fd, NULL);
+        if (r < 0)
+                return r;
+
+        if (new_fs_size < old_fs_size) {
+
+                /* Shrink the LUKS device now, matching the new file system size */
+                r = crypt_resize(setup->crypt_device, setup->dm_name, new_fs_size / 512);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to shrink LUKS device: %m");
+
+                log_info("LUKS device shrinking completed.");
+
+                if (S_ISREG(st.st_mode)) {
+                        /* Shrink the image file */
+                        if (ftruncate(image_fd, new_image_size) < 0)
+                                return log_error_errno(errno, "Failed to shrink image file %s: %m", ip);
+
+                        log_info("Shrinking of image file completed.");
+                }
+
+                /* Refresh the loop devices size */
+                r = loop_device_refresh_size(setup->loop, UINT64_MAX, new_partition_size);
+                if (r == -ENOTTY)
+                        log_debug_errno(r, "Device is not a loopback device, not refreshing size.");
+                else if (r < 0)
+                        return log_error_errno(r, "Failed to refresh loopback device size: %m");
+                else
+                        log_info("Refreshing loop device size completed.");
+
+                r = apply_resize_partition(image_fd, disk_uuid, table);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        log_info("Shrinking of partition completed.");
+
+                if (ioctl(image_fd, BLKRRPART, 0) < 0)
+                        log_debug_errno(errno, "BLKRRPART failed on block device, ignoring: %m");
+        } else {
+                r = home_store_embedded_identity(new_home, setup->root_fd, h->uid, embedded_home);
+                if (r < 0)
+                        return r;
+        }
+
+        r = home_store_header_identity_luks(new_home, setup, header_home);
+        if (r < 0)
+                return r;
+
+        r = home_extend_embedded_identity(new_home, h, setup);
+        if (r < 0)
+                return r;
+
+        if (user_record_luks_discard(h))
+                (void) run_fitrim(setup->root_fd);
+
+        r = home_sync_and_statfs(setup->root_fd, &sfs);
+        if (r < 0)
+                return r;
+
+        r = home_setup_undo(setup);
+        if (r < 0)
+                return r;
+
+        log_info("Everything completed.");
+
+        print_size_summary(new_image_size, new_fs_size, &sfs);
+
+        *ret_home = TAKE_PTR(new_home);
+        return 0;
+}
+
+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 */) {
+
+        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;
+        int r;
+
+        assert(h);
+        assert(user_record_storage(h) == USER_LUKS);
+        assert(setup);
+
+        type = crypt_get_type(setup->crypt_device);
+        if (!type)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine crypto device type.");
+
+        r = crypt_keyslot_max(type);
+        if (r <= 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine number of key slots.");
+        max_key_slots = r;
+
+        r = crypt_get_volume_key_size(setup->crypt_device);
+        if (r <= 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine volume key size.");
+        volume_key_size = (size_t) r;
+
+        volume_key = malloc(volume_key_size);
+        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.");
+        }
+        if (r < 0)
+                return log_error_errno(r, "Failed to unlocks LUKS superblock: %m");
+
+        n_effective = strv_length(effective_passwords);
+
+        build_good_pbkdf(&good_pbkdf, h);
+        build_minimal_pbkdf(&minimal_pbkdf, h);
+
+        for (i = 0; i < max_key_slots; i++) {
+                r = crypt_keyslot_destroy(setup->crypt_device, i);
+                if (r < 0 && !IN_SET(r, -ENOENT, -EINVAL)) /* Returns EINVAL or ENOENT if there's no key in this slot already */
+                        return log_error_errno(r, "Failed to destroy LUKS password: %m");
+
+                if (i >= n_effective) {
+                        if (r >= 0)
+                                log_info("Destroyed LUKS key slot %zu.", i);
+                        continue;
+                }
+
+                if (strv_find(pkcs11_decrypted_passwords, effective_passwords[i])) {
+                        log_debug("Using minimal PBKDF for slot %zu", i);
+                        r = crypt_set_pbkdf_type(setup->crypt_device, &minimal_pbkdf);
+                } else {
+                        log_debug("Using good PBKDF for slot %zu", i);
+                        r = crypt_set_pbkdf_type(setup->crypt_device, &good_pbkdf);
+                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to tweak PBKDF for slot %zu: %m", i);
+
+                r = crypt_keyslot_add_by_volume_key(
+                                setup->crypt_device,
+                                i,
+                                volume_key,
+                                volume_key_size,
+                                effective_passwords[i],
+                                strlen(effective_passwords[i]));
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set up LUKS password: %m");
+
+                log_info("Updated LUKS key slot %zu.", i);
+        }
+
+        return 1;
+}
+
+int home_lock_luks(UserRecord *h) {
+        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
+        _cleanup_close_ int root_fd = -1;
+        const char *p;
+        int r;
+
+        assert(h);
+
+        assert_se(p = user_record_home_directory(h));
+        root_fd = open(p, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
+        if (root_fd < 0)
+                return log_error_errno(errno, "Failed to open home directory: %m");
+
+        r = make_dm_names(h->user_name, &dm_name, &dm_node);
+        if (r < 0)
+                return r;
+
+        r = crypt_init_by_name(&cd, dm_name);
+        if (r < 0)
+                return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", dm_name);
+
+        log_info("Discovered used LUKS device %s.", dm_node);
+        crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
+
+        if (syncfs(root_fd) < 0) /* Snake oil, but let's better be safe than sorry */
+                return log_error_errno(errno, "Failed to synchronize file system %s: %m", p);
+
+        root_fd = safe_close(root_fd);
+
+        log_info("File system synchronized.");
+
+        /* Note that we don't invoke FIFREEZE here, it appears libcryptsetup/device-mapper already does that on its own for us */
+
+        r = crypt_suspend(cd, dm_name);
+        if (r < 0)
+                return log_error_errno(r, "Failed to suspend cryptsetup device: %s: %m", dm_node);
+
+        log_info("LUKS device suspended.");
+        return 0;
+}
+
+static int luks_try_resume(
+                struct crypt_device *cd,
+                const char *dm_name,
+                char **password) {
+
+        char **pp;
+        int r;
+
+        assert(cd);
+        assert(dm_name);
+
+        STRV_FOREACH(pp, password) {
+                r = crypt_resume_by_passphrase(
+                                cd,
+                                dm_name,
+                                CRYPT_ANY_SLOT,
+                                *pp,
+                                strlen(*pp));
+                if (r >= 0) {
+                        log_info("Resumed LUKS device %s.", dm_name);
+                        return 0;
+                }
+
+                log_debug_errno(r, "Password %zu didn't work for resuming device: %m", (size_t) (pp - password));
+        }
+
+        return -ENOKEY;
+}
+
+int home_unlock_luks(UserRecord *h, char ***pkcs11_decrypted_passwords) {
+        _cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
+        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        int r;
+
+        assert(h);
+
+        r = make_dm_names(h->user_name, &dm_name, &dm_node);
+        if (r < 0)
+                return r;
+
+        r = crypt_init_by_name(&cd, dm_name);
+        if (r < 0)
+                return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", dm_name);
+
+        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.");
+        }
+        if (r < 0)
+                return log_error_errno(r, "Failed to resume LUKS superblock: %m");
+
+        log_info("LUKS device resumed.");
+        return 0;
+}
diff --git a/src/home/homework-luks.h b/src/home/homework-luks.h
new file mode 100644 (file)
index 0000000..581255a
--- /dev/null
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "crypt-util.h"
+#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_activate_luks(UserRecord *h, char ***pkcs11_decrypted_passwords, UserRecord **ret_home);
+int home_deactivate_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_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_passwd_luks(UserRecord *h, HomeSetup *setup, char **pkcs11_decrypted_passwords, char **effective_passwords);
+
+int home_lock_luks(UserRecord *h);
+int home_unlock_luks(UserRecord *h, char ***pkcs11_decrypted_passwords);
+
+static inline uint64_t luks_volume_key_size_convert(struct crypt_device *cd) {
+        int k;
+
+        assert(cd);
+
+        /* Convert the "int" to uint64_t, which we usually use for byte sizes stored on disk. */
+
+        k = crypt_get_volume_key_size(cd);
+        if (k <= 0)
+                return UINT64_MAX;
+
+        return (uint64_t) k;
+}
diff --git a/src/home/homework-mount.c b/src/home/homework-mount.c
new file mode 100644 (file)
index 0000000..9e11168
--- /dev/null
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sched.h>
+#include <sys/mount.h>
+
+#include "alloc-util.h"
+#include "homework-mount.h"
+#include "mkdir.h"
+#include "mount-util.h"
+#include "path-util.h"
+#include "string-util.h"
+
+static const char *mount_options_for_fstype(const char *fstype) {
+        if (streq(fstype, "ext4"))
+                return "noquota,user_xattr";
+        if (streq(fstype, "xfs"))
+                return "noquota";
+        if (streq(fstype, "btrfs"))
+                return "noacl";
+        return NULL;
+}
+
+int home_mount_node(const char *node, const char *fstype, bool discard) {
+        _cleanup_free_ char *joined = NULL;
+        const char *options, *discard_option;
+        int r;
+
+        options = mount_options_for_fstype(fstype);
+
+        discard_option = discard ? "discard" : "nodiscard";
+
+        if (options) {
+                joined = strjoin(options, ",", discard_option);
+                if (!joined)
+                        return log_oom();
+
+                options = joined;
+        } else
+                options = discard_option;
+
+        r = mount_verbose(LOG_ERR, node, "/run/systemd/user-home-mount", fstype, MS_NODEV|MS_NOSUID|MS_RELATIME, strempty(options));
+        if (r < 0)
+                return r;
+
+        log_info("Mounting file system completed.");
+        return 0;
+}
+
+int home_unshare_and_mount(const char *node, const char *fstype, bool discard) {
+        int r;
+
+        if (unshare(CLONE_NEWNS) < 0)
+                return log_error_errno(errno, "Couldn't unshare file system namespace: %m");
+
+        r = mount_verbose(LOG_ERR, "/run", "/run", NULL, MS_SLAVE|MS_REC, NULL); /* Mark /run as MS_SLAVE in our new namespace */
+        if (r < 0)
+                return r;
+
+        (void) mkdir_p("/run/systemd/user-home-mount", 0700);
+
+        if (node)
+                return home_mount_node(node, fstype, discard);
+
+        return 0;
+}
+
+int home_move_mount(const char *user_name_and_realm, const char *target) {
+        _cleanup_free_ char *subdir = NULL;
+        const char *d;
+        int r;
+
+        assert(user_name_and_realm);
+        assert(target);
+
+        if (user_name_and_realm) {
+                subdir = path_join("/run/systemd/user-home-mount/", user_name_and_realm);
+                if (!subdir)
+                        return log_oom();
+
+                d = subdir;
+        } else
+                d = "/run/systemd/user-home-mount/";
+
+        (void) mkdir_p(target, 0700);
+
+        r = mount_verbose(LOG_ERR, d, target, NULL, MS_BIND, NULL);
+        if (r < 0)
+                return r;
+
+        r = umount_verbose("/run/systemd/user-home-mount");
+        if (r < 0)
+                return r;
+
+        log_info("Moving to final mount point %s completed.", target);
+        return 0;
+}
diff --git a/src/home/homework-mount.h b/src/home/homework-mount.h
new file mode 100644 (file)
index 0000000..d926756
--- /dev/null
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#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_move_mount(const char *user_name_and_realm, const char *target);
diff --git a/src/home/homework-pkcs11.c b/src/home/homework-pkcs11.c
new file mode 100644 (file)
index 0000000..941ba23
--- /dev/null
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "hexdecoct.h"
+#include "homework-pkcs11.h"
+#include "pkcs11-util.h"
+#include "strv.h"
+
+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) void *decrypted_key = NULL;
+        struct pkcs11_callback_data *data = userdata;
+        _cleanup_free_ char *token_label = NULL;
+        CK_TOKEN_INFO updated_token_info;
+        size_t decrypted_key_size;
+        CK_OBJECT_HANDLE object;
+        char **i;
+        CK_RV rv;
+        int r;
+
+        assert(m);
+        assert(slot_info);
+        assert(token_info);
+        assert(uri);
+        assert(data);
+
+        /* Special return values:
+         *
+         * -ENOANO       → if we need a PIN but have none
+         * -ERFKILL      → if a "protected authentication path" is needed but we have no OK to use it
+         * -EOWNERDEAD   → if the PIN is locked
+         * -ENOLCK       → if the supplied PIN is incorrect
+         * -ETOOMANYREFS → ditto, but only a few tries left
+         * -EUCLEAN      → ditto, but only a single try left
+         */
+
+        token_label = pkcs11_token_label(token_info);
+        if (!token_label)
+                return log_oom();
+
+        if (FLAGS_SET(token_info->flags, CKF_PROTECTED_AUTHENTICATION_PATH)) {
+
+                if (data->secret->pkcs11_protected_authentication_path_permitted <= 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(ERFKILL), "Security token requires authentication through protected authentication path.");
+
+                rv = m->C_Login(session, CKU_USER, NULL, 0);
+                if (rv != CKR_OK)
+                        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);
+                goto decrypt;
+        }
+
+        if (!FLAGS_SET(token_info->flags, CKF_LOGIN_REQUIRED)) {
+                log_info("No login into security token '%s' required.", token_label);
+                goto decrypt;
+        }
+
+        if (strv_isempty(data->secret->pkcs11_pin))
+                return log_error_errno(SYNTHETIC_ERRNO(ENOANO), "Security Token requires PIN.");
+
+        STRV_FOREACH(i, data->secret->pkcs11_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);
+                        goto decrypt;
+                }
+                if (rv == CKR_PIN_LOCKED)
+                        return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD), "PIN of security token is blocked. Please unblock it first.");
+                if (!IN_SET(rv, CKR_PIN_INCORRECT, CKR_PIN_LEN_RANGE))
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
+        }
+
+        rv = m->C_GetTokenInfo(slot_id, &updated_token_info);
+        if (rv != CKR_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire updated security token information for slot %lu: %s", slot_id, p11_kit_strerror(rv));
+
+        if (FLAGS_SET(updated_token_info.flags, CKF_USER_PIN_FINAL_TRY))
+                return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN), "PIN of security token incorrect, only a single try left.");
+        if (FLAGS_SET(updated_token_info.flags, CKF_USER_PIN_COUNT_LOW))
+                return log_error_errno(SYNTHETIC_ERRNO(ETOOMANYREFS), "PIN of security token incorrect, only a few tries left.");
+
+        return log_error_errno(SYNTHETIC_ERRNO(ENOLCK), "PIN of security token incorrect.");
+
+decrypt:
+        r = pkcs11_token_find_private_key(m, session, uri, &object);
+        if (r < 0)
+                return r;
+
+        r = pkcs11_token_decrypt_data(m, session, object, data->encrypted_key->data, data->encrypted_key->size, &decrypted_key, &decrypted_key_size);
+        if (r < 0)
+                return r;
+
+        if (base64mem(decrypted_key, decrypted_key_size, &data->decrypted_password) < 0)
+                return log_oom();
+
+        return 1;
+}
diff --git a/src/home/homework-pkcs11.h b/src/home/homework-pkcs11.h
new file mode 100644 (file)
index 0000000..469ba71
--- /dev/null
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if HAVE_P11KIT
+#include "memory-util.h"
+#include "user-record.h"
+#include "pkcs11-util.h"
+
+struct pkcs11_callback_data {
+        UserRecord *user_record;
+        UserRecord *secret;
+        Pkcs11EncryptedKey *encrypted_key;
+        char *decrypted_password;
+};
+
+static inline void pkcs11_callback_data_release(struct pkcs11_callback_data *data) {
+        erase_and_free(data->decrypted_password);
+}
+
+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);
+#endif
diff --git a/src/home/homework-quota.c b/src/home/homework-quota.c
new file mode 100644 (file)
index 0000000..ba3917b
--- /dev/null
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <sys/quota.h>
+
+#include "blockdev-util.h"
+#include "btrfs-util.h"
+#include "errno-util.h"
+#include "format-util.h"
+#include "homework-quota.h"
+#include "missing_magic.h"
+#include "quota-util.h"
+#include "stat-util.h"
+#include "user-util.h"
+
+int home_update_quota_btrfs(UserRecord *h, const char *path) {
+        int r;
+
+        assert(h);
+        assert(path);
+
+        if (h->disk_size == UINT64_MAX)
+                return 0;
+
+        /* If the user wants quota, enable it */
+        r = btrfs_quota_enable(path, true);
+        if (r == -ENOTTY)
+                return log_error_errno(r, "No btrfs quota support on subvolume %s.", path);
+        if (r < 0)
+                return log_error_errno(r, "Failed to enable btrfs quota support on %s.", path);
+
+        r = btrfs_qgroup_set_limit(path, 0, h->disk_size);
+        if (r < 0)
+                return log_error_errno(r, "Faled to set disk quota on subvolume %s: %m", path);
+
+        log_info("Set btrfs quota.");
+
+        return 0;
+}
+
+int home_update_quota_classic(UserRecord *h, const char *path) {
+        struct dqblk req;
+        dev_t devno;
+        int r;
+
+        assert(h);
+        assert(uid_is_valid(h->uid));
+        assert(path);
+
+        if (h->disk_size == UINT64_MAX)
+                return 0;
+
+        r = get_block_device(path, &devno);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine block device of %s: %m", path);
+        if (devno == 0)
+                return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system %s not backed by a block device.", path);
+
+        r = quotactl_devno(QCMD_FIXED(Q_GETQUOTA, USRQUOTA), devno, h->uid, &req);
+        if (r < 0) {
+                if (ERRNO_IS_NOT_SUPPORTED(r))
+                        return log_error_errno(r, "No UID quota support on %s.", path);
+
+                if (r != -ESRCH)
+                        return log_error_errno(r, "Failed to query disk quota for UID " UID_FMT ": %m", h->uid);
+
+                zero(req);
+        } else {
+                /* Shortcut things if everything is set up properly already */
+                if (FLAGS_SET(req.dqb_valid, QIF_BLIMITS) && h->disk_size / QIF_DQBLKSIZE == req.dqb_bhardlimit) {
+                        log_info("Configured quota already matches the intended setting, not updating quota.");
+                        return 0;
+                }
+        }
+
+        req.dqb_valid = QIF_BLIMITS;
+        req.dqb_bsoftlimit = req.dqb_bhardlimit = h->disk_size / QIF_DQBLKSIZE;
+
+        r = quotactl_devno(QCMD_FIXED(Q_SETQUOTA, USRQUOTA), devno, h->uid, &req);
+        if (r < 0) {
+                if (r == -ESRCH)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "UID quota not available on %s.", path);
+
+                return log_error_errno(r, "Failed to set disk quota for UID " UID_FMT ": %m", h->uid);
+        }
+
+        log_info("Updated per-UID quota.");
+
+        return 0;
+}
+
+int home_update_quota_auto(UserRecord *h, const char *path) {
+        struct statfs sfs;
+        int r;
+
+        assert(h);
+
+        if (h->disk_size == UINT64_MAX)
+                return 0;
+
+        if (!path) {
+                path = user_record_image_path(h);
+                if (!path)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Home record lacks image path.");
+        }
+
+        if (statfs(path, &sfs) < 0)
+                return log_error_errno(errno, "Failed to statfs() file system: %m");
+
+        if (is_fs_type(&sfs, XFS_SB_MAGIC) ||
+            is_fs_type(&sfs, EXT4_SUPER_MAGIC))
+                return home_update_quota_classic(h, path);
+
+        if (is_fs_type(&sfs, BTRFS_SUPER_MAGIC)) {
+
+                r = btrfs_is_subvol(path);
+                if (r < 0)
+                        return log_error_errno(errno, "Failed to test if %s is a subvolume: %m", path);
+                if (r == 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Directory %s is not a subvolume, cannot apply quota.", path);
+
+                return home_update_quota_btrfs(h, path);
+        }
+
+        return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Type of directory %s not known, cannot apply quota.", path);
+}
diff --git a/src/home/homework-quota.h b/src/home/homework-quota.h
new file mode 100644 (file)
index 0000000..e6cc16d
--- /dev/null
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "user-record.h"
+
+int home_update_quota_btrfs(UserRecord *h, const char *path);
+int home_update_quota_classic(UserRecord *h, const char *path);
+int home_update_quota_auto(UserRecord *h, const char *path);
diff --git a/src/home/homework.c b/src/home/homework.c
new file mode 100644 (file)
index 0000000..ecf07ff
--- /dev/null
@@ -0,0 +1,1482 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <stddef.h>
+#include <sys/mount.h>
+
+#include "chown-recursive.h"
+#include "copy.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "home-util.h"
+#include "homework-cifs.h"
+#include "homework-directory.h"
+#include "homework-fscrypt.h"
+#include "homework-luks.h"
+#include "homework-mount.h"
+#include "homework-pkcs11.h"
+#include "homework.h"
+#include "main-func.h"
+#include "memory-util.h"
+#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"
+#include "tmpfile-util.h"
+#include "user-util.h"
+#include "virt.h"
+
+/* Make sure a bad password always results in a 3s delay, no matter what */
+#define BAD_PASSWORD_DELAY_USEC (3 * USEC_PER_SEC)
+
+int user_record_authenticate(
+                UserRecord *h,
+                UserRecord *secret,
+                char ***pkcs11_decrypted_passwords) {
+
+        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;
+        int r;
+
+        assert(h);
+        assert(secret);
+
+        /* 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.
+         *
+         * 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. */
+
+        /* First, let's see if the supplied plain-text passwords work? */
+        r = user_record_test_secret(h, secret);
+        if (r == -ENOKEY) {
+                log_info_errno(r, "None of the supplied plaintext passwords unlocks the user record's hashed passwords.");
+                need_password = true;
+        } else if (r == -ENXIO)
+                log_debug_errno(r, "User record has no hashed passwords, plaintext passwords not tested.");
+        else if (r < 0)
+                return log_error_errno(r, "Failed to validate password of record: %m");
+        else {
+                log_info("Provided password unlocks user record.");
+                return 0;
+        }
+
+        /* 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,
+                };
+                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);
+                        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 0;
+                        }
+                }
+
+                r = pkcs11_find_token(data.encrypted_key->uri, pkcs11_callback, &data);
+                switch (r) {
+                case -EAGAIN:
+                        need_token = true;
+                        break;
+                case -ENOANO:
+                        need_pin = true;
+                        break;
+                case -ERFKILL:
+                        need_protected_authentication_path_permitted = true;
+                        break;
+                case -EOWNERDEAD:
+                        pin_locked = true;
+                        break;
+                case -ENOLCK:
+                        pin_incorrect = true;
+                        break;
+                case -ETOOMANYREFS:
+                        pin_incorrect = pin_incorrect_few_tries_left = true;
+                        break;
+                case -EUCLEAN:
+                        pin_incorrect = pin_incorrect_few_tries_left = pin_incorrect_one_try_left = true;
+                        break;
+                default:
+                        if (r < 0)
+                                return r;
+
+                        r = test_password_one(data.encrypted_key->hashed_password, data.decrypted_password);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to test PKCS#11 password: %m");
+                        if (r == 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Configured PKCS#11 security token %s does not decrypt encrypted key correctly.", data.encrypted_key->uri);
+
+                        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);
+                        if (r < 0)
+                                return log_oom();
+
+                        return 0;
+                }
+#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;
+        if (pin_incorrect_few_tries_left)
+                return -ETOOMANYREFS;
+        if (pin_incorrect)
+                return -ENOLCK;
+        if (pin_locked)
+                return -EOWNERDEAD;
+        if (need_protected_authentication_path_permitted)
+                return -ERFKILL;
+        if (need_pin)
+                return -ENOANO;
+        if (need_token)
+                return -EBADSLT;
+        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.");
+}
+
+int home_setup_undo(HomeSetup *setup) {
+        int r = 0, q;
+
+        assert(setup);
+
+        setup->root_fd = safe_close(setup->root_fd);
+
+        if (setup->undo_mount) {
+                q = umount_verbose("/run/systemd/user-home-mount");
+                if (q < 0)
+                        r = q;
+        }
+
+        if (setup->undo_dm && setup->crypt_device && setup->dm_name) {
+                q = crypt_deactivate(setup->crypt_device, setup->dm_name);
+                if (q < 0)
+                        r = q;
+        }
+
+        setup->undo_mount = false;
+        setup->undo_dm = false;
+
+        setup->dm_name = mfree(setup->dm_name);
+        setup->dm_node = mfree(setup->dm_node);
+
+        setup->loop = loop_device_unref(setup->loop);
+        crypt_free(setup->crypt_device);
+        setup->crypt_device = NULL;
+
+        explicit_bzero_safe(setup->volume_key, setup->volume_key_size);
+        setup->volume_key = mfree(setup->volume_key);
+        setup->volume_key_size = 0;
+
+        return r;
+}
+
+int home_prepare(
+                UserRecord *h,
+                bool already_activated,
+                char ***pkcs11_decrypted_passwords,
+                HomeSetup *setup,
+                UserRecord **ret_header_home) {
+
+        int r;
+
+        assert(h);
+        assert(setup);
+        assert(!setup->loop);
+        assert(!setup->crypt_device);
+        assert(setup->root_fd < 0);
+        assert(!setup->undo_dm);
+        assert(!setup->undo_mount);
+
+        /* Makes a home directory accessible (through the root_fd file descriptor, not by path!). */
+
+        switch (user_record_storage(h)) {
+
+        case USER_LUKS:
+                return home_prepare_luks(h, already_activated, NULL, pkcs11_decrypted_passwords, setup, ret_header_home);
+
+        case USER_SUBVOLUME:
+        case USER_DIRECTORY:
+                r = home_prepare_directory(h, already_activated, setup);
+                break;
+
+        case USER_FSCRYPT:
+                r = home_prepare_fscrypt(h, already_activated, pkcs11_decrypted_passwords, setup);
+                break;
+
+        case USER_CIFS:
+                r = home_prepare_cifs(h, already_activated, setup);
+                break;
+
+        default:
+                return log_error_errno(SYNTHETIC_ERRNO(ENOLINK), "Processing home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
+        }
+
+        if (r < 0)
+                return r;
+
+        if (ret_header_home)
+                *ret_header_home = NULL;
+
+        return r;
+}
+
+int home_sync_and_statfs(int root_fd, struct statfs *ret) {
+        assert(root_fd >= 0);
+
+        /* Let's sync this to disk, so that the disk space reported by fstatfs() below is accurate (for file
+         * systems such as btrfs where this is determined lazily). */
+
+        if (syncfs(root_fd) < 0)
+                return log_error_errno(errno, "Failed to synchronize file system: %m");
+
+        if (ret)
+                if (fstatfs(root_fd, ret) < 0)
+                        return log_error_errno(errno, "Failed to statfs() file system: %m");
+
+        log_info("Synchronized disk.");
+
+        return 0;
+}
+
+static int read_identity_file(int root_fd, JsonVariant **ret) {
+        _cleanup_(fclosep) FILE *identity_file = NULL;
+        _cleanup_close_ int identity_fd = -1;
+        unsigned line, column;
+        int r;
+
+        assert(root_fd >= 0);
+        assert(ret);
+
+        identity_fd = openat(root_fd, ".identity", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
+        if (identity_fd < 0)
+                return log_error_errno(errno, "Failed to open .identity file in home directory: %m");
+
+        r = fd_verify_regular(identity_fd);
+        if (r < 0)
+                return log_error_errno(r, "Embedded identity file is not a regular file, refusing: %m");
+
+        identity_file = 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);
+
+        log_info("Read embedded .identity file.");
+
+        return 0;
+}
+
+static int write_identity_file(int root_fd, JsonVariant *v, uid_t uid) {
+        _cleanup_(json_variant_unrefp) JsonVariant *normalized = NULL;
+        _cleanup_(fclosep) FILE *identity_file = NULL;
+        _cleanup_close_ int identity_fd = -1;
+        _cleanup_free_ char *fn = NULL;
+        int r;
+
+        assert(root_fd >= 0);
+        assert(v);
+
+        normalized = json_variant_ref(v);
+
+        r = json_variant_normalize(&normalized);
+        if (r < 0)
+                log_warning_errno(r, "Failed to normalize user record, ignoring: %m");
+
+        r = tempfn_random(".identity", NULL, &fn);
+        if (r < 0)
+                return r;
+
+        identity_fd = openat(root_fd, fn, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
+        if (identity_fd < 0)
+                return log_error_errno(errno, "Failed to create .identity file in home directory: %m");
+
+        identity_file = 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);
+        if (r < 0) {
+                log_error_errno(r, "Failed to write .identity file: %m");
+                goto fail;
+        }
+
+        if (fchown(fileno(identity_file), uid, uid) < 0) {
+                log_error_errno(r, "Failed to change ownership of identity file: %m");
+                goto fail;
+        }
+
+        if (renameat(root_fd, fn, root_fd, ".identity") < 0) {
+                r = log_error_errno(errno, "Failed to move identity file into place: %m");
+                goto fail;
+        }
+
+        log_info("Wrote embedded .identity file.");
+
+        return 0;
+
+fail:
+        (void) unlinkat(root_fd, fn, 0);
+        return r;
+}
+
+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) {
+
+        _cleanup_(user_record_unrefp) UserRecord *embedded_home = NULL, *intermediate_home = NULL, *new_home = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        int r;
+
+        assert(h);
+        assert(root_fd >= 0);
+
+        r = read_identity_file(root_fd, &v);
+        if (r < 0)
+                return r;
+
+        embedded_home = user_record_new();
+        if (!embedded_home)
+                return log_oom();
+
+        r = user_record_load(embedded_home, v, USER_RECORD_LOAD_EMBEDDED);
+        if (r < 0)
+                return r;
+
+        if (!user_record_compatible(h, embedded_home))
+                return log_error_errno(SYNTHETIC_ERRNO(EREMCHG), "Hmbedded 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);
+        if (r < 0)
+                return r;
+
+        /* At this point we have three records to deal with:
+         *
+         *      · The record we got passed from the host
+         *      · The record included in the LUKS header (only if LUKS is used)
+         *      · The record in the home directory itself (~.identity)
+         *
+         *  Now we have to reconcile all three, and let the newest one win. */
+
+        if (header_home) {
+                /* Note we relax the requirements here. Instead of insisting that the host record is strictly
+                 * newer, let's also be OK if its equally new. If it is, we'll however insist that the
+                 * embedded record must be newer, so that we update at least one of the two. */
+
+                r = user_record_reconcile(h, header_home, mode == USER_RECONCILE_REQUIRE_NEWER ? USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL : mode, &intermediate_home);
+                if (r == -EREMCHG) /* this was supposed to be checked earlier already, but let's check this again */
+                        return log_error_errno(r, "Identity stored on host and in header don't match, refusing.");
+                if (r == -ESTALE)
+                        return log_error_errno(r, "Embedded identity record is newer than supplied record, refusing.");
+                if (r < 0)
+                        return log_error_errno(r, "Failed to reconcile host and header identities: %m");
+                if (r == USER_RECONCILE_EMBEDDED_WON)
+                        log_info("Reconciling header user identity completed (header version was newer).");
+                else if (r == USER_RECONCILE_HOST_WON) {
+                        log_info("Reconciling header user identity completed (host version was newer).");
+
+                        if (mode == USER_RECONCILE_REQUIRE_NEWER) /* Host version is newer than the header
+                                                                   * version, hence we'll update
+                                                                   * something. This means we can relax the
+                                                                   * requirements on the embedded
+                                                                   * identity. */
+                                mode = USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL;
+                } else {
+                        assert(r == USER_RECONCILE_IDENTICAL);
+                        log_info("Reconciling user identities completed (host and header version were identical).");
+                }
+
+                h = intermediate_home;
+        }
+
+        r = user_record_reconcile(h, embedded_home, mode, &new_home);
+        if (r == -EREMCHG)
+                return log_error_errno(r, "Identity stored on host and in home don't match, refusing.");
+        if (r == -ESTALE)
+                return log_error_errno(r, "Embedded identity record is equally new or newer than supplied record, refusing.");
+        if (r < 0)
+                return log_error_errno(r, "Failed to reconcile host and embedded identities: %m");
+        if (r == USER_RECONCILE_EMBEDDED_WON)
+                log_info("Reconciling embedded user identity completed (embedded version was newer).");
+        else if (r == USER_RECONCILE_HOST_WON)
+                log_info("Reconciling embedded user identity completed (host version was newer).");
+        else {
+                assert(r == USER_RECONCILE_IDENTICAL);
+                log_info("Reconciling embedded user identity completed (host and embedded version were identical).");
+        }
+
+        if (ret_embedded_home)
+                *ret_embedded_home = TAKE_PTR(embedded_home);
+
+        if (ret_new_home)
+                *ret_new_home = TAKE_PTR(new_home);
+
+        return 0;
+}
+
+int home_store_embedded_identity(UserRecord *h, int root_fd, uid_t uid, UserRecord *old_home) {
+        _cleanup_(user_record_unrefp) UserRecord *embedded = NULL;
+        int r;
+
+        assert(h);
+        assert(root_fd >= 0);
+        assert(uid_is_valid(uid));
+
+        r = user_record_clone(h, USER_RECORD_EXTRACT_EMBEDDED, &embedded);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine new embedded record: %m");
+
+        if (old_home && user_record_equal(old_home, embedded)) {
+                log_debug("Not updating embedded home record.");
+                return 0;
+        }
+
+        /* The identity has changed, let's update it in the image */
+        r = write_identity_file(root_fd, embedded->json, h->uid);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+static const char *file_system_type_fd(int fd) {
+        struct statfs sfs;
+
+        assert(fd >= 0);
+
+        if (fstatfs(fd, &sfs) < 0) {
+                log_debug_errno(errno, "Failed to statfs(): %m");
+                return NULL;
+        }
+
+        if (is_fs_type(&sfs, XFS_SB_MAGIC))
+                return "xfs";
+        if (is_fs_type(&sfs, EXT4_SUPER_MAGIC))
+                return "ext4";
+        if (is_fs_type(&sfs, BTRFS_SUPER_MAGIC))
+                return "btrfs";
+
+        return NULL;
+}
+
+int home_extend_embedded_identity(UserRecord *h, UserRecord *used, HomeSetup *setup) {
+        int r;
+
+        assert(h);
+        assert(used);
+        assert(setup);
+
+        r = user_record_add_binding(
+                        h,
+                        user_record_storage(used),
+                        user_record_image_path(used),
+                        setup->found_partition_uuid,
+                        setup->found_luks_uuid,
+                        setup->found_fs_uuid,
+                        setup->crypt_device ? crypt_get_cipher(setup->crypt_device) : NULL,
+                        setup->crypt_device ? crypt_get_cipher_mode(setup->crypt_device) : NULL,
+                        setup->crypt_device ? luks_volume_key_size_convert(setup->crypt_device) : UINT64_MAX,
+                        file_system_type_fd(setup->root_fd),
+                        user_record_home_directory(used),
+                        used->uid,
+                        (gid_t) used->uid);
+        if (r < 0)
+                return log_error_errno(r, "Failed to update binding in record: %m");
+
+        return 0;
+}
+
+static int chown_recursive_directory(int root_fd, uid_t uid) {
+        int r;
+
+        assert(root_fd >= 0);
+        assert(uid_is_valid(uid));
+
+        r = fd_chown_recursive(root_fd, uid, (gid_t) uid, 0777);
+        if (r < 0)
+                return log_error_errno(r, "Failed to change ownership of files and directories: %m");
+        if (r == 0)
+                log_info("Recursive changing of ownership not necessary, skipped.");
+        else
+                log_info("Recursive changing of ownership completed.");
+
+        return 0;
+}
+
+int home_refresh(
+                UserRecord *h,
+                HomeSetup *setup,
+                UserRecord *header_home,
+                char ***pkcs11_decrypted_passwords,
+                struct statfs *ret_statfs,
+                UserRecord **ret_new_home) {
+
+        _cleanup_(user_record_unrefp) UserRecord *embedded_home = NULL, *new_home = NULL;
+        int r;
+
+        assert(h);
+        assert(setup);
+        assert(ret_new_home);
+
+        /* 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);
+        if (r < 0)
+                return r;
+
+        r = home_store_header_identity_luks(new_home, setup, header_home);
+        if (r < 0)
+                return r;
+
+        r = home_store_embedded_identity(new_home, setup->root_fd, h->uid, embedded_home);
+        if (r < 0)
+                return r;
+
+        r = chown_recursive_directory(setup->root_fd, h->uid);
+        if (r < 0)
+                return r;
+
+        r = home_sync_and_statfs(setup->root_fd, ret_statfs);
+        if (r < 0)
+                return r;
+
+        *ret_new_home = TAKE_PTR(new_home);
+        return 0;
+}
+
+static int home_activate(UserRecord *h, UserRecord **ret_home) {
+        _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
+        _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
+        int r;
+
+        assert(h);
+
+        if (!h->user_name)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks user name, refusing.");
+        if (!uid_is_valid(h->uid))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks UID, refusing.");
+        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);
+        if (r < 0)
+                return r;
+
+        r = user_record_test_home_directory_and_warn(h);
+        if (r < 0)
+                return r;
+        if (r == USER_TEST_MOUNTED)
+                return log_error_errno(SYNTHETIC_ERRNO(EALREADY), "Home directory %s is already mounted, refusing.", user_record_home_directory(h));
+
+        r = user_record_test_image_path_and_warn(h);
+        if (r < 0)
+                return r;
+        if (r == USER_TEST_ABSENT)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Image path %s is missing, refusing.", user_record_image_path(h));
+
+        switch (user_record_storage(h)) {
+
+        case USER_LUKS:
+                r = home_activate_luks(h, &pkcs11_decrypted_passwords, &new_home);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case USER_SUBVOLUME:
+        case USER_DIRECTORY:
+        case USER_FSCRYPT:
+                r = home_activate_directory(h, &pkcs11_decrypted_passwords, &new_home);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case USER_CIFS:
+                r = home_activate_cifs(h, &pkcs11_decrypted_passwords, &new_home);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        default:
+                assert_not_reached("unexpected type");
+        }
+
+        /* Note that the returned object might either be a reference to an updated version of the existing
+         * home object, or a reference to a newly allocated home object. The caller has to be able to deal
+         * with both, and consider the old object out-of-date. */
+        if (user_record_equal(h, new_home)) {
+                *ret_home = NULL;
+                return 0; /* no identity change */
+        }
+
+        *ret_home = TAKE_PTR(new_home);
+        return 1; /* identity updated */
+}
+
+static int home_deactivate(UserRecord *h, bool force) {
+        bool done = false;
+        int r;
+
+        assert(h);
+
+        if (!h->user_name)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record incomplete, refusing.");
+        if (!IN_SET(user_record_storage(h), USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS))
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Deactivating home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
+
+        r = user_record_test_home_directory_and_warn(h);
+        if (r < 0)
+                return r;
+        if (r == USER_TEST_MOUNTED) {
+                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));
+
+                log_info("Unmounting completed.");
+                done = true;
+        } else
+                log_info("Directory %s is already unmounted.", user_record_home_directory(h));
+
+        if (user_record_storage(h) == USER_LUKS) {
+                r = home_deactivate_luks(h);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        done = true;
+        }
+
+        if (!done)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOEXEC), "Home is not active.");
+
+        log_info("Everything completed.");
+        return 0;
+}
+
+static int copy_skel(int root_fd, const char *skel) {
+        int r;
+
+        assert(root_fd >= 0);
+
+        r = copy_tree_at(AT_FDCWD, skel, root_fd, ".", UID_INVALID, GID_INVALID, COPY_MERGE|COPY_REPLACE);
+        if (r == -ENOENT) {
+                log_info("Skeleton directory %s missing, ignoring.", skel);
+                return 0;
+        }
+        if (r < 0)
+                return log_error_errno(r, "Failed to copy in %s: %m", skel);
+
+        log_info("Copying in %s completed.", skel);
+        return 0;
+}
+
+static int change_access_mode(int root_fd, mode_t m) {
+        assert(root_fd >= 0);
+
+        if (fchmod(root_fd, m) < 0)
+                return log_error_errno(errno, "Failed to change access mode of top-level directory: %m");
+
+        log_info("Changed top-level directory access mode to 0%o.", m);
+        return 0;
+}
+
+int home_populate(UserRecord *h, int dir_fd) {
+        int r;
+
+        assert(h);
+        assert(dir_fd >= 0);
+
+        r = copy_skel(dir_fd, user_record_skeleton_directory(h));
+        if (r < 0)
+                return r;
+
+        r = home_store_embedded_identity(h, dir_fd, h->uid, NULL);
+        if (r < 0)
+                return r;
+
+        r = chown_recursive_directory(dir_fd, h->uid);
+        if (r < 0)
+                return r;
+
+        r = change_access_mode(dir_fd, user_record_access_mode(h));
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static int user_record_compile_effective_passwords(
+                UserRecord *h,
+                char ***ret_effective_passwords,
+                char ***ret_pkcs11_decrypted_passwords) {
+
+        _cleanup_(strv_free_erasep) char **effective = NULL, **pkcs11_passwords = NULL;
+        size_t n;
+        char **i;
+        int r;
+
+        assert(h);
+
+        /* 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
+         * the old literal password only (and do not care for the old PKCS#11 token) */
+
+        if (strv_isempty(h->hashed_password))
+                return log_error_errno(EINVAL, "User record has no hashed passwords, refusing.");
+
+        /* Generates the list of plaintext passwords to propagate to LUKS/fscrypt devices, and checks whether
+         * we have a plaintext password for each hashed one. If we are missing one we'll fail, since we
+         * couldn't sync fscrypt/LUKS to the login account properly. */
+
+        STRV_FOREACH(i, h->hashed_password) {
+                bool found = false;
+                char **j;
+
+                log_debug("Looking for plaintext password for: %s", *i);
+
+                /* Let's scan all provided plaintext passwords */
+                STRV_FOREACH(j, h->password) {
+                        r = test_password_one(*i, *j);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to test plain text password: %m");
+                        if (r > 0) {
+                                if (ret_effective_passwords) {
+                                        r = strv_extend(&effective, *j);
+                                        if (r < 0)
+                                                return log_oom();
+                                }
+
+                                log_debug("Found literal plaintext password.");
+                                found = true;
+                                break;
+                        }
+                }
+
+                if (!found)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Missing plaintext password for defined hashed password");
+        }
+
+        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 = h,
+                        .encrypted_key = h->pkcs11_encrypted_key + n,
+                };
+
+                r = pkcs11_find_token(data.encrypted_key->uri, pkcs11_callback, &data);
+                if (r == -EAGAIN)
+                        return -EBADSLT;
+                if (r < 0)
+                        return r;
+
+                r = test_password_one(data.encrypted_key->hashed_password, data.decrypted_password);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to test PKCS#11 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, data.decrypted_password);
+                        if (r < 0)
+                                return log_oom();
+                }
+
+                if (ret_pkcs11_decrypted_passwords) {
+                        r = strv_extend(&pkcs11_passwords, data.decrypted_password);
+                        if (r < 0)
+                                return log_oom();
+                }
+#else
+                return -EBADSLT;
+#endif
+        }
+
+        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 home_create(UserRecord *h, UserRecord **ret_home) {
+        _cleanup_(strv_free_erasep) char **effective_passwords = NULL, **pkcs11_decrypted_passwords = NULL;
+        _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
+        int r;
+
+        assert(h);
+
+        if (!h->user_name)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks name, refusing.");
+        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);
+        if (r < 0)
+                return r;
+
+        r = user_record_test_home_directory_and_warn(h);
+        if (r < 0)
+                return r;
+        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();
+                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");
+
+                        new_storage = r > 0 ? USER_SUBVOLUME : USER_DIRECTORY;
+                } else
+                        new_storage = USER_LUKS;
+
+                r = user_record_add_binding(
+                                h,
+                                new_storage,
+                                NULL,
+                                SD_ID128_NULL,
+                                SD_ID128_NULL,
+                                SD_ID128_NULL,
+                                NULL,
+                                NULL,
+                                UINT64_MAX,
+                                NULL,
+                                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);
+        if (r < 0)
+                return r;
+        if (!IN_SET(r, USER_TEST_ABSENT, USER_TEST_UNDEFINED, USER_TEST_MAYBE))
+                return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Image path %s already exists, refusing.", user_record_image_path(h));
+
+        switch (user_record_storage(h)) {
+
+        case USER_LUKS:
+                r = home_create_luks(h, pkcs11_decrypted_passwords, effective_passwords, &new_home);
+                break;
+
+        case USER_DIRECTORY:
+        case USER_SUBVOLUME:
+                r = home_create_directory_or_subvolume(h, &new_home);
+                break;
+
+        case USER_FSCRYPT:
+                r = home_create_fscrypt(h, effective_passwords, &new_home);
+                break;
+
+        case USER_CIFS:
+                r = home_create_cifs(h, &new_home);
+                break;
+
+        default:
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTTY),
+                                       "Creating home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
+        }
+        if (r < 0)
+                return r;
+
+        if (user_record_equal(h, new_home)) {
+                *ret_home = NULL;
+                return 0;
+        }
+
+        *ret_home = TAKE_PTR(new_home);
+        return 1;
+}
+
+static int home_remove(UserRecord *h) {
+        bool deleted = false;
+        const char *ip, *hd;
+        int r;
+
+        assert(h);
+
+        if (!h->user_name)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks user name, refusing.");
+        if (!IN_SET(user_record_storage(h), USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS))
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Removing home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
+
+        hd = user_record_home_directory(h);
+
+        r = user_record_test_home_directory_and_warn(h);
+        if (r < 0)
+                return r;
+        if (r == USER_TEST_MOUNTED)
+                return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Directory %s is still mounted, refusing.", hd);
+
+        assert(hd);
+
+        r = user_record_test_image_path_and_warn(h);
+        if (r < 0)
+                return r;
+
+        ip = user_record_image_path(h);
+
+        switch (user_record_storage(h)) {
+
+        case USER_LUKS: {
+                struct stat st;
+
+                assert(ip);
+
+                if (stat(ip, &st) < 0) {
+                        if (errno != -ENOENT)
+                                return log_error_errno(errno, "Failed to stat %s: %m", ip);
+
+                } else {
+                        if (S_ISREG(st.st_mode)) {
+                                if (unlink(ip) < 0) {
+                                        if (errno != ENOENT)
+                                                return log_error_errno(errno, "Failed to remove %s: %m", ip);
+                                } else
+                                        deleted = true;
+
+                        } else if (S_ISBLK(st.st_mode))
+                                log_info("Not removing file system on block device %s.", ip);
+                        else
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Image file %s is neither block device, nor regular, refusing removal.", ip);
+                }
+
+                break;
+        }
+
+        case USER_SUBVOLUME:
+        case USER_DIRECTORY:
+        case USER_FSCRYPT:
+                assert(ip);
+
+                r = rm_rf(ip, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+                if (r < 0) {
+                        if (r != -ENOENT)
+                                return log_warning_errno(r, "Failed to remove %s: %m", ip);
+                } else
+                        deleted = true;
+
+                /* If the image path and the home directory are the same invalidate the home directory, so
+                 * that we don't remove it anymore */
+                if (path_equal(ip, hd))
+                        hd = NULL;
+
+                break;
+
+        case USER_CIFS:
+                /* Nothing else to do here: we won't remove remote stuff. */
+                log_info("Not removing home directory on remote server.");
+                break;
+
+        default:
+                assert_not_reached("unknown storage type");
+        }
+
+        if (hd) {
+                if (rmdir(hd) < 0) {
+                        if (errno != ENOENT)
+                                return log_error_errno(errno, "Failed to remove %s, ignoring: %m", hd);
+                } else
+                        deleted = true;
+        }
+
+        if (deleted)
+                log_info("Everything completed.");
+        else {
+                log_notice("Nothing to remove.");
+                return -EALREADY;
+        }
+
+        return 0;
+}
+
+static int home_validate_update(UserRecord *h, HomeSetup *setup) {
+        bool has_mount = false;
+        int r;
+
+        assert(h);
+        assert(setup);
+
+        if (!h->user_name)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks user name, refusing.");
+        if (!uid_is_valid(h->uid))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks UID, refusing.");
+        if (!IN_SET(user_record_storage(h), USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS))
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Processing home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
+
+        r = user_record_test_home_directory_and_warn(h);
+        if (r < 0)
+                return r;
+
+        has_mount = r == USER_TEST_MOUNTED;
+
+        r = user_record_test_image_path_and_warn(h);
+        if (r < 0)
+                return r;
+        if (r == USER_TEST_ABSENT)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Image path %s does not exist", user_record_image_path(h));
+
+        switch (user_record_storage(h)) {
+
+        case USER_DIRECTORY:
+        case USER_SUBVOLUME:
+        case USER_FSCRYPT:
+        case USER_CIFS:
+                break;
+
+        case USER_LUKS: {
+                r = home_validate_update_luks(h, setup);
+                if (r < 0)
+                        return r;
+                if ((r > 0) != has_mount)
+                        return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Home mount incompletely set up.");
+
+                break;
+        }
+
+        default:
+                assert_not_reached("unexpected storage type");
+        }
+
+        return has_mount; /* return true if the home record is already active */
+}
+
+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;
+        bool already_activated = false;
+        int r;
+
+        assert(h);
+        assert(ret);
+
+        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+        if (r < 0)
+                return r;
+
+        r = home_validate_update(h, &setup);
+        if (r < 0)
+                return r;
+
+        already_activated = r > 0;
+
+        r = home_prepare(h, already_activated, &pkcs11_decrypted_passwords, &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);
+        if (r < 0)
+                return r;
+
+        r = home_store_header_identity_luks(new_home, &setup, header_home);
+        if (r < 0)
+                return r;
+
+        r = home_store_embedded_identity(new_home, setup.root_fd, h->uid, embedded_home);
+        if (r < 0)
+                return r;
+
+        r = home_extend_embedded_identity(new_home, h, &setup);
+        if (r < 0)
+                return r;
+
+        r = home_sync_and_statfs(setup.root_fd, NULL);
+        if (r < 0)
+                return r;
+
+        r = home_setup_undo(&setup);
+        if (r < 0)
+                return r;
+
+        log_info("Everything completed.");
+
+        *ret = TAKE_PTR(new_home);
+        return 0;
+}
+
+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;
+        bool already_activated = false;
+        int r;
+
+        assert(h);
+        assert(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);
+        if (r < 0)
+                return r;
+
+        r = home_validate_update(h, &setup);
+        if (r < 0)
+                return r;
+
+        already_activated = r > 0;
+
+        switch (user_record_storage(h)) {
+
+        case USER_LUKS:
+                return home_resize_luks(h, already_activated, &pkcs11_decrypted_passwords, &setup, ret);
+
+        case USER_DIRECTORY:
+        case USER_SUBVOLUME:
+        case USER_FSCRYPT:
+                return home_resize_directory(h, already_activated, &pkcs11_decrypted_passwords, &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)));
+        }
+}
+
+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_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+        bool already_activated = false;
+        int r;
+
+        assert(h);
+        assert(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);
+        if (r < 0)
+                return r;
+
+        r = home_validate_update(h, &setup);
+        if (r < 0)
+                return r;
+
+        already_activated = r > 0;
+
+        r = home_prepare(h, already_activated, &pkcs11_decrypted_passwords, &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);
+        if (r < 0)
+                return r;
+
+        switch (user_record_storage(h)) {
+
+        case USER_LUKS:
+                r = home_passwd_luks(h, &setup, pkcs11_decrypted_passwords, effective_passwords);
+                if (r < 0)
+                        return r;
+                break;
+
+        case USER_FSCRYPT:
+                r = home_passwd_fscrypt(h, &setup, pkcs11_decrypted_passwords, effective_passwords);
+                if (r < 0)
+                        return r;
+                break;
+
+        default:
+                break;
+        }
+
+        r = home_store_header_identity_luks(new_home, &setup, header_home);
+        if (r < 0)
+                return r;
+
+        r = home_store_embedded_identity(new_home, setup.root_fd, h->uid, embedded_home);
+        if (r < 0)
+                return r;
+
+        r = home_extend_embedded_identity(new_home, h, &setup);
+        if (r < 0)
+                return r;
+
+        r = home_sync_and_statfs(setup.root_fd, NULL);
+        if (r < 0)
+                return r;
+
+        r = home_setup_undo(&setup);
+        if (r < 0)
+                return r;
+
+        log_info("Everything completed.");
+
+        *ret_home = TAKE_PTR(new_home);
+        return 1;
+}
+
+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;
+        bool already_activated = false;
+        int r;
+
+        assert(h);
+        assert(ret_home);
+
+        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+        if (r < 0)
+                return r;
+
+        r = home_validate_update(h, &setup);
+        if (r < 0)
+                return r;
+
+        already_activated = r > 0;
+
+        r = home_prepare(h, already_activated, &pkcs11_decrypted_passwords, &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);
+        if (r < 0)
+                return r;
+
+        r = home_extend_embedded_identity(new_home, h, &setup);
+        if (r < 0)
+                return r;
+
+        r = home_setup_undo(&setup);
+        if (r < 0)
+                return r;
+
+        log_info("Everything completed.");
+
+        *ret_home = TAKE_PTR(new_home);
+        return 1;
+}
+
+static int home_lock(UserRecord *h) {
+        int r;
+
+        assert(h);
+
+        if (!h->user_name)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record incomplete, refusing.");
+        if (user_record_storage(h) != USER_LUKS)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Locking home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
+
+        r = user_record_test_home_directory_and_warn(h);
+        if (r < 0)
+                return r;
+        if (r != USER_TEST_MOUNTED)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOEXEC), "Home directory of %s is not mounted, can't lock.", h->user_name);
+
+        r = home_lock_luks(h);
+        if (r < 0)
+                return r;
+
+        log_info("Everything completed.");
+        return 1;
+}
+
+static int home_unlock(UserRecord *h) {
+        _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
+        int r;
+
+        assert(h);
+
+        if (!h->user_name)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record incomplete, refusing.");
+        if (user_record_storage(h) != USER_LUKS)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Unlocking home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(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);
+        if (r < 0)
+                return r;
+
+        r = home_unlock_luks(h, &pkcs11_decrypted_passwords);
+        if (r < 0)
+                return r;
+
+        log_info("Everything completed.");
+        return 1;
+}
+
+static int run(int argc, char *argv[]) {
+        _cleanup_(user_record_unrefp) UserRecord *home = NULL, *new_home = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        _cleanup_(fclosep) FILE *opened_file = NULL;
+        unsigned line = 0, column = 0;
+        const char *json_path = NULL;
+        FILE *json_file;
+        usec_t start;
+        int r;
+
+        start = now(CLOCK_MONOTONIC);
+
+        log_setup_service();
+
+        umask(0022);
+
+        if (argc < 2 || argc > 3)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes one or two arguments.");
+
+        if (argc > 2) {
+                json_path = argv[2];
+
+                opened_file = fopen(json_path, "re");
+                if (!opened_file)
+                        return log_error_errno(errno, "Failed to open %s: %m", json_path);
+
+                json_file = opened_file;
+        } else {
+                json_path = "<stdin>";
+                json_file = stdin;
+        }
+
+        r = json_parse_file(json_file, json_path, JSON_PARSE_SENSITIVE, &v, &line, &column);
+        if (r < 0)
+                return log_error_errno(r, "[%s:%u:%u] Failed to parse JSON data: %m", json_path, line, column);
+
+        home = user_record_new();
+        if (!home)
+                return log_oom();
+
+        r = user_record_load(home, v, USER_RECORD_LOAD_FULL|USER_RECORD_LOG);
+        if (r < 0)
+                return r;
+
+        /* 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
+         * ERANGE          → file system size too small
+         * ENOLINK         → system does not support selected storage backend
+         * EPROTONOSUPPORT → system does not support selected file system
+         * ENOTTY          → operation not support on this storage
+         * 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
+         * 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
+         * 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
+         */
+
+        if (streq(argv[1], "activate"))
+                r = home_activate(home, &new_home);
+        else if (streq(argv[1], "deactivate"))
+                r = home_deactivate(home, false);
+        else if (streq(argv[1], "deactivate-force"))
+                r = home_deactivate(home, true);
+        else if (streq(argv[1], "create"))
+                r = home_create(home, &new_home);
+        else if (streq(argv[1], "remove"))
+                r = home_remove(home);
+        else if (streq(argv[1], "update"))
+                r = home_update(home, &new_home);
+        else if (streq(argv[1], "resize"))
+                r = home_resize(home, &new_home);
+        else if (streq(argv[1], "passwd"))
+                r = home_passwd(home, &new_home);
+        else if (streq(argv[1], "inspect"))
+                r = home_inspect(home, &new_home);
+        else if (streq(argv[1], "lock"))
+                r = home_lock(home);
+        else if (streq(argv[1], "unlock"))
+                r = home_unlock(home);
+        else
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown verb '%s'.", argv[1]);
+        if (r == -ENOKEY && !strv_isempty(home->password)) { /* There were passwords specified but they were incorrect */
+                usec_t end, n, d;
+
+                /* Make sure bad password replies always take at least 3s, and if longer multiples of 3s, so
+                 * that it's not clear how long we actually needed for our calculations. */
+                n = now(CLOCK_MONOTONIC);
+                assert(n >= start);
+
+                d = usec_sub_unsigned(n, start);
+                if (d > BAD_PASSWORD_DELAY_USEC)
+                        end = start + DIV_ROUND_UP(d, BAD_PASSWORD_DELAY_USEC) * BAD_PASSWORD_DELAY_USEC;
+                else
+                        end = start + BAD_PASSWORD_DELAY_USEC;
+
+                if (n < end)
+                        (void) usleep(usec_sub_unsigned(end, n));
+        }
+        if (r < 0)
+                return r;
+
+        /* We always pass the new record back, regardless if it changed or not. This allows our caller to
+         * prepare a fresh record, send to us, and only if it works use it without having to keep a local
+         * copy. */
+        if (new_home)
+                json_variant_dump(new_home->json, JSON_FORMAT_NEWLINE, stdout, NULL);
+
+        return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/home/homework.h b/src/home/homework.h
new file mode 100644 (file)
index 0000000..81698b7
--- /dev/null
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <linux/fs.h>
+#include <sys/vfs.h>
+
+#include "sd-id128.h"
+
+#include "loop-util.h"
+#include "user-record.h"
+#include "user-record-util.h"
+
+typedef struct HomeSetup {
+        char *dm_name;
+        char *dm_node;
+
+        LoopDevice *loop;
+        struct crypt_device *crypt_device;
+        int root_fd;
+        sd_id128_t found_partition_uuid;
+        sd_id128_t found_luks_uuid;
+        sd_id128_t found_fs_uuid;
+
+        uint8_t fscrypt_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
+
+        void *volume_key;
+        size_t volume_key_size;
+
+        bool undo_dm;
+        bool undo_mount;
+
+        uint64_t partition_offset;
+        uint64_t partition_size;
+} HomeSetup;
+
+#define HOME_SETUP_INIT                                 \
+        {                                               \
+                .root_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_refresh(UserRecord *h, HomeSetup *setup, UserRecord *header_home, char ***pkcs11_decrypted_passwords, 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_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 home_sync_and_statfs(int root_fd, struct statfs *ret);
diff --git a/src/home/meson.build b/src/home/meson.build
new file mode 100644 (file)
index 0000000..eb6da0b
--- /dev/null
@@ -0,0 +1,81 @@
+# SPDX-License-Identifier: LGPL-2.1+
+
+systemd_homework_sources = files('''
+        home-util.c
+        home-util.h
+        homework-cifs.c
+        homework-cifs.h
+        homework-directory.c
+        homework-directory.h
+        homework-fscrypt.c
+        homework-fscrypt.h
+        homework-luks.c
+        homework-luks.h
+        homework-mount.c
+        homework-mount.h
+        homework-pkcs11.h
+        homework-quota.c
+        homework-quota.h
+        homework.c
+        homework.h
+        user-record-util.c
+        user-record-util.h
+'''.split())
+
+if conf.get('HAVE_P11KIT') == 1
+        systemd_homework_sources += files('homework-pkcs11.c')
+endif
+
+systemd_homed_sources = files('''
+        home-util.c
+        home-util.h
+        homed-bus.c
+        homed-bus.h
+        homed-home-bus.c
+        homed-home-bus.h
+        homed-home.c
+        homed-home.h
+        homed-manager-bus.c
+        homed-manager-bus.h
+        homed-manager.c
+        homed-manager.h
+        homed-operation.c
+        homed-operation.h
+        homed-varlink.c
+        homed-varlink.h
+        homed.c
+        pwquality-util.c
+        pwquality-util.h
+        user-record-sign.c
+        user-record-sign.h
+        user-record-util.c
+        user-record-util.h
+'''.split())
+
+homectl_sources = files('''
+        home-util.c
+        home-util.h
+        homectl.c
+        pwquality-util.c
+        pwquality-util.h
+        user-record-util.c
+        user-record-util.h
+'''.split())
+
+pam_systemd_home_sym = 'src/home/pam_systemd_home.sym'
+pam_systemd_home_c = files('''
+        home-util.c
+        home-util.h
+        pam_systemd_home.c
+        user-record-util.c
+        user-record-util.h
+'''.split())
+
+if conf.get('ENABLE_HOMED') == 1
+        install_data('org.freedesktop.home1.conf',
+                     install_dir : dbuspolicydir)
+        install_data('org.freedesktop.home1.service',
+                     install_dir : dbussystemservicedir)
+        install_data('org.freedesktop.home1.policy',
+                     install_dir : polkitpolicydir)
+endif
diff --git a/src/home/org.freedesktop.home1.conf b/src/home/org.freedesktop.home1.conf
new file mode 100644 (file)
index 0000000..d615501
--- /dev/null
@@ -0,0 +1,193 @@
+<?xml version="1.0"?> <!--*-nxml-*-->
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+        "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<busconfig>
+
+        <policy user="root">
+                <allow own="org.freedesktop.home1"/>
+                <allow send_destination="org.freedesktop.home1"/>
+                <allow receive_sender="org.freedesktop.home1"/>
+        </policy>
+
+        <policy context="default">
+                <deny send_destination="org.freedesktop.home1"/>
+
+                <!-- generic interfaces -->
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.DBus.Introspectable"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.DBus.Peer"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.DBus.Properties"
+                       send_member="Get"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.DBus.Properties"
+                       send_member="GetAll"/>
+
+                <!-- Manager object -->
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="GetHomeByName"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="GetHomeByUID"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="GetUserRecordByName"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="GetUserRecordByUID"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="ListHomes"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="ActivateHome"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="DeactivateHome"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="RegisterHome"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="UnregisterHome"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="CreateHome"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="RealizeHome"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="RemoveHome"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="FixateHome"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="AuthenticateHome"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="UpdateHome"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="ResizeHome"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="ChangePasswordHome"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="LockHome"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="UnlockHome"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="AcquireHome"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="RefHome"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="ReleaseHome"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Manager"
+                       send_member="LockAllHomes"/>
+
+                <!-- Home object -->
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Home"
+                       send_member="Activate"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Home"
+                       send_member="Deactivate"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Home"
+                       send_member="Unregister"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Home"
+                       send_member="Realize"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Home"
+                       send_member="Remove"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Home"
+                       send_member="Fixate"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Home"
+                       send_member="Authenticate"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Home"
+                       send_member="Update"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Home"
+                       send_member="Resize"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Home"
+                       send_member="ChangePassword"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Home"
+                       send_member="Lock"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Home"
+                       send_member="Unlock"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Home"
+                       send_member="Acquire"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Home"
+                       send_member="Ref"/>
+
+                <allow send_destination="org.freedesktop.home1"
+                       send_interface="org.freedesktop.home1.Home"
+                       send_member="Release"/>
+
+                <allow receive_sender="org.freedesktop.home1"/>
+        </policy>
+
+</busconfig>
diff --git a/src/home/org.freedesktop.home1.policy b/src/home/org.freedesktop.home1.policy
new file mode 100644 (file)
index 0000000..43a373d
--- /dev/null
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
+<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+        "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
+
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<policyconfig>
+
+        <vendor>The systemd Project</vendor>
+        <vendor_url>http://www.freedesktop.org/wiki/Software/systemd</vendor_url>
+
+        <action id="org.freedesktop.home1.create-home">
+                <description gettext-domain="systemd">Create a home area</description>
+                <message gettext-domain="systemd">Authentication is required to create a user's home area.</message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+        <action id="org.freedesktop.home1.remove-home">
+                <description gettext-domain="systemd">Remove a home area</description>
+                <message gettext-domain="systemd">Authentication is required to remove a user's home area.</message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+        <action id="org.freedesktop.home1.authenticate-home">
+                <description gettext-domain="systemd">Check credentials of a home area</description>
+                <message gettext-domain="systemd">Authentication is required to check credentials against a user's home area.</message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+        <action id="org.freedesktop.home1.update-home">
+                <description gettext-domain="systemd">Update a home area</description>
+                <message gettext-domain="systemd">Authentication is required to update a user's home area.</message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+        <action id="org.freedesktop.home1.resize-home">
+                <description gettext-domain="systemd">Resize a home area</description>
+                <message gettext-domain="systemd">Authentication is required to resize a user's home area.</message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+        <action id="org.freedesktop.home1.passwd-home">
+                <description gettext-domain="systemd">Change password of a home area</description>
+                <message gettext-domain="systemd">Authentication is required to change the password of a user's home area.</message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+</policyconfig>
diff --git a/src/home/org.freedesktop.home1.service b/src/home/org.freedesktop.home1.service
new file mode 100644 (file)
index 0000000..cff19b3
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1+
+
+[D-BUS Service]
+Name=org.freedesktop.home1
+Exec=/bin/false
+User=root
+SystemdService=dbus-org.freedesktop.home1.service
diff --git a/src/home/pam_systemd_home.c b/src/home/pam_systemd_home.c
new file mode 100644 (file)
index 0000000..a2235bf
--- /dev/null
@@ -0,0 +1,951 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <security/pam_ext.h>
+#include <security/pam_modules.h>
+
+#include "sd-bus.h"
+
+#include "bus-common-errors.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "home-util.h"
+#include "memory-util.h"
+#include "pam-util.h"
+#include "parse-util.h"
+#include "strv.h"
+#include "user-record-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,
+                bool *please_suspend,
+                bool *debug) {
+
+        int i;
+
+        assert(argc >= 0);
+        assert(argc == 0 || argv);
+
+        for (i = 0; i < argc; i++) {
+                const char *v;
+
+                if ((v = startswith(argv[1], "suspend="))) {
+                        int k;
+
+                        k = parse_boolean(v);
+                        if (k < 0)
+                                pam_syslog(handle, LOG_WARNING, "Failed to parse suspend-please= argument, ignoring: %s", v);
+                        else if (please_suspend)
+                                *please_suspend = k;
+
+                } else if ((v = startswith(argv[i], "debug="))) {
+                        int k;
+
+                        k = parse_boolean(v);
+                        if (k < 0)
+                                pam_syslog(handle, LOG_WARNING, "Failed to parse debug= argument, ignoring: %s", v);
+                        else if (debug)
+                                *debug = k;
+
+                } else
+                        pam_syslog(handle, LOG_WARNING, "Unknown parameter '%s', ignoring", argv[i]);
+        }
+
+        return 0;
+}
+
+static int acquire_user_record(
+                pam_handle_t *handle,
+                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;
+        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 (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
+         * user names we don't consider valid. */
+        if (STR_IN_SET(username, "root", NOBODY_USER_NAME) || !valid_user_group_name(username))
+                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);
+        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));
+                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) {
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+                _cleanup_free_ char *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);
+                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)) {
+                                pam_syslog(handle, LOG_DEBUG, "systemd-homed is not available: %s", bus_error_message(&error, r));
+                                goto user_unknown;
+                        }
+
+                        if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_HOME)) {
+                                pam_syslog(handle, LOG_DEBUG, "Not a user managed by systemd-homed: %s", bus_error_message(&error, r));
+                                goto user_unknown;
+                        }
+
+                        pam_syslog(handle, LOG_ERR, "Failed to query user record: %s", bus_error_message(&error, r));
+                        return PAM_SERVICE_ERR;
+                }
+
+                r = sd_bus_message_read(reply, "sbo", &json, NULL, NULL);
+                if (r < 0)
+                        return pam_bus_log_parse_error(handle, r);
+
+                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);
+                if (r != PAM_SUCCESS) {
+                        pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data: %s", pam_strerror(handle, r));
+                        return r;
+                }
+
+                TAKE_PTR(json_copy);
+
+                r = pam_set_data(handle, "systemd-user-record-is-homed", USER_RECORD_IS_HOMED, NULL);
+                if (r != PAM_SUCCESS) {
+                        pam_syslog(handle, LOG_ERR, "Failed to set PAM user record is homed flag: %s", pam_strerror(handle, r));
+                        return r;
+                }
+        }
+
+        r = json_parse(json, JSON_PARSE_SENSITIVE, &v, NULL, NULL);
+        if (r < 0) {
+                pam_syslog(handle, LOG_ERR, "Failed to parse JSON user record: %s", strerror_safe(r));
+                return PAM_SERVICE_ERR;
+        }
+
+        ur = user_record_new();
+        if (!ur)
+                return pam_log_oom(handle);
+
+        r = user_record_load(ur, v, USER_RECORD_LOAD_REFUSE_SECRET);
+        if (r < 0) {
+                pam_syslog(handle, LOG_ERR, "Failed to load user record: %s", strerror_safe(r));
+                return PAM_SERVICE_ERR;
+        }
+
+        if (!streq_ptr(username, ur->user_name)) {
+                pam_syslog(handle, LOG_ERR, "Acquired user record does not match user name.");
+                return PAM_SERVICE_ERR;
+        }
+
+        if (ret_record)
+                *ret_record = TAKE_PTR(ur);
+
+        return PAM_SUCCESS;
+
+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);
+        if (r != PAM_SUCCESS)
+                pam_syslog(handle, LOG_ERR, "Failed to set PAM user record is homed flag, ignoring: %s", pam_strerror(handle, r));
+
+        return PAM_USER_UNKNOWN;
+}
+
+static int release_user_record(pam_handle_t *handle) {
+        int r, k;
+
+        r = pam_set_data(handle, "systemd-user-record", NULL, NULL);
+        if (r != PAM_SUCCESS)
+                pam_syslog(handle, LOG_ERR, "Failed to release PAM user record data: %s", pam_strerror(handle, r));
+
+        k = pam_set_data(handle, "systemd-user-record-is-homed", 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));
+
+        return IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA) ? k : r;
+}
+
+static void cleanup_home_fd(pam_handle_t *handle, void *data, int error_status) {
+        safe_close(PTR_TO_FD(data));
+}
+
+static int handle_generic_user_record_error(
+                pam_handle_t *handle,
+                const char *user_name,
+                UserRecord *secret,
+                int ret,
+                const sd_bus_error *error) {
+
+        assert(user_name);
+        assert(secret);
+        assert(error);
+
+        int r;
+
+        /* Logs about all errors, except for PAM_CONV_ERR, i.e. when requesting more info failed. */
+
+        if (sd_bus_error_has_name(error, BUS_ERROR_HOME_ABSENT)) {
+                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Home of user %s is currently absent, please plug in the necessary storage device or backing file system.", user_name);
+                pam_syslog(handle, LOG_ERR, "Failed to acquire home for user %s: %s", user_name, bus_error_message(error, ret));
+                return PAM_PERM_DENIED;
+
+        } else if (sd_bus_error_has_name(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT)) {
+                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Too frequent unsuccessful login attempts for user %s, try again later.", user_name);
+                pam_syslog(handle, LOG_ERR, "Failed to acquire home for user %s: %s", user_name, bus_error_message(error, ret));
+                return PAM_MAXTRIES;
+
+        } else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_PASSWORD)) {
+                _cleanup_(erase_and_freep) char *newp = NULL;
+
+                /* This didn't work? Ask for an (additional?) password */
+
+                if (strv_isempty(secret->password))
+                        r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Password: ");
+                else
+                        r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Password incorrect or not sufficient for authentication of user %s, please try again: ", user_name);
+                if (r != PAM_SUCCESS)
+                        return PAM_CONV_ERR; /* no logging here */
+
+                if (isempty(newp)) {
+                        pam_syslog(handle, LOG_DEBUG, "Password request aborted.");
+                        return PAM_AUTHTOK_ERR;
+                }
+
+                r = user_record_set_password(secret, STRV_MAKE(newp), true);
+                if (r < 0) {
+                        pam_syslog(handle, LOG_ERR, "Failed to store password: %s", strerror_safe(r));
+                        return PAM_SERVICE_ERR;
+                }
+
+        } else if (sd_bus_error_has_name(error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN)) {
+                _cleanup_(erase_and_freep) char *newp = NULL;
+
+                if (strv_isempty(secret->password))
+                        r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Security token of user %s not inserted, please enter password: ", user_name);
+                else
+                        r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Password incorrect or not sufficient, and configured security token of user %s not inserted, please enter password: ", user_name);
+                if (r != PAM_SUCCESS)
+                        return PAM_CONV_ERR; /* no logging here */
+
+                if (isempty(newp)) {
+                        pam_syslog(handle, LOG_DEBUG, "Password request aborted.");
+                        return PAM_AUTHTOK_ERR;
+                }
+
+                r = user_record_set_password(secret, STRV_MAKE(newp), true);
+                if (r < 0) {
+                        pam_syslog(handle, LOG_ERR, "Failed to store password: %s", strerror_safe(r));
+                        return PAM_SERVICE_ERR;
+                }
+
+        } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_NEEDED)) {
+                _cleanup_(erase_and_freep) char *newp = NULL;
+
+                r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Please enter security token PIN: ");
+                if (r != PAM_SUCCESS)
+                        return PAM_CONV_ERR; /* no logging here */
+
+                if (isempty(newp)) {
+                        pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
+                        return PAM_AUTHTOK_ERR;
+                }
+
+                r = user_record_set_pkcs11_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;
+                }
+
+        } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED)) {
+
+                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Please authenticate physically on security token of user %s.", user_name);
+
+                r = user_record_set_pkcs11_protected_authentication_path_permitted(secret, true);
+                if (r < 0) {
+                        pam_syslog(handle, LOG_ERR, "Failed to set PKCS#11 protected authentication path permitted flag: %s", strerror_safe(r));
+                        return PAM_SERVICE_ERR;
+                }
+
+        } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN)) {
+                _cleanup_(erase_and_freep) char *newp = NULL;
+
+                r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Security token PIN incorrect, please enter PIN for security token of user %s again: ", user_name);
+                if (r != PAM_SUCCESS)
+                        return PAM_CONV_ERR; /* no logging here */
+
+                if (isempty(newp)) {
+                        pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
+                        return PAM_AUTHTOK_ERR;
+                }
+
+                r = user_record_set_pkcs11_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;
+                }
+
+        } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT)) {
+                _cleanup_(erase_and_freep) char *newp = NULL;
+
+                r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Security token PIN incorrect (only a few tries left!), please enter PIN for security token of user %s again: ", user_name);
+                if (r != PAM_SUCCESS)
+                        return PAM_CONV_ERR; /* no logging here */
+
+                if (isempty(newp)) {
+                        pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
+                        return PAM_AUTHTOK_ERR;
+                }
+
+                r = user_record_set_pkcs11_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;
+                }
+
+        } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT)) {
+                _cleanup_(erase_and_freep) char *newp = NULL;
+
+                r = pam_prompt(handle, PAM_PROMPT_ECHO_OFF, &newp, "Security token PIN incorrect (only one try left!), please enter PIN for security token of user %s again: ", user_name);
+                if (r != PAM_SUCCESS)
+                        return PAM_CONV_ERR; /* no logging here */
+
+                if (isempty(newp)) {
+                        pam_syslog(handle, LOG_DEBUG, "PIN request aborted.");
+                        return PAM_AUTHTOK_ERR;
+                }
+
+                r = user_record_set_pkcs11_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;
+                }
+
+        } else {
+                pam_syslog(handle, LOG_ERR, "Failed to acquire home for user %s: %s", user_name, bus_error_message(error, ret));
+                return PAM_SERVICE_ERR;
+        }
+
+        return PAM_SUCCESS;
+}
+
+static int acquire_home(
+                pam_handle_t *handle,
+                bool please_authenticate,
+                bool please_suspend,
+                bool debug) {
+
+        _cleanup_(user_record_unrefp) UserRecord *ur = NULL, *secret = NULL;
+        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;
+        const void *home_fd_ptr = NULL;
+        unsigned n_attempts = 0;
+        int r;
+
+        assert(handle);
+
+        /* This acquires a reference to a home directory in one of two ways: if please_authenticate is true,
+         * then we'll call AcquireHome() after asking the user for a password. Otherwise it tries to call
+         * RefHome() and if that fails queries the user for a password and uses AcquireHome().
+         *
+         * The idea is that the PAM authentication hook sets please_authenticate and thus always
+         * 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. */
+
+        /* If we already have acquired the fd, let's shortcut this */
+        r = pam_get_data(handle, "systemd-home-fd", &home_fd_ptr);
+        if (r == PAM_SUCCESS && PTR_TO_INT(home_fd_ptr) >= 0)
+                return PAM_SUCCESS;
+
+        r = pam_acquire_bus_connection(handle, &bus);
+        if (r != PAM_SUCCESS)
+                return r;
+
+        r = acquire_user_record(handle, &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
+         * 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
+         * treat bad passwords as a request to collect one more password and pass the new all all previously
+         * used passwords again. */
+
+        for (;;) {
+                _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+
+                if (do_auth && !secret) {
+                        const char *cached_password = NULL;
+
+                        secret = user_record_new();
+                        if (!secret)
+                                return pam_log_oom(handle);
+
+                        /* If there's already a cached password, use it. But if not let's authenticate
+                         * without anything, maybe some other authentication mechanism systemd-homed
+                         * implements (such as PKCS#11) allows us to authenticate without anything else. */
+                        r = pam_get_item(handle, PAM_AUTHTOK, (const void**) &cached_password);
+                        if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
+                                pam_syslog(handle, LOG_ERR, "Failed to get cached password: %s", pam_strerror(handle, r));
+                                return r;
+                        }
+
+                        if (!isempty(cached_password)) {
+                                r = user_record_set_password(secret, STRV_MAKE(cached_password), true);
+                                if (r < 0) {
+                                        pam_syslog(handle, LOG_ERR, "Failed to store password: %s", strerror_safe(r));
+                                        return PAM_SERVICE_ERR;
+                                }
+                        }
+                }
+
+                r = sd_bus_message_new_method_call(
+                                bus,
+                                &m,
+                                "org.freedesktop.home1",
+                                "/org/freedesktop/home1",
+                                "org.freedesktop.home1.Manager",
+                                do_auth ? "AcquireHome" : "RefHome");
+                if (r < 0)
+                        return pam_bus_log_create_error(handle, r);
+
+                r = sd_bus_message_append(m, "s", ur->user_name);
+                if (r < 0)
+                        return pam_bus_log_create_error(handle, r);
+
+                if (do_auth) {
+                        r = bus_message_append_secret(m, secret);
+                        if (r < 0)
+                                return pam_bus_log_create_error(handle, r);
+                }
+
+                r = sd_bus_message_append(m, "b", please_suspend);
+                if (r < 0)
+                        return pam_bus_log_create_error(handle, r);
+
+                r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
+                if (r < 0) {
+
+                        if (sd_bus_error_has_name(&error, BUS_ERROR_HOME_NOT_ACTIVE))
+                                /* Only on RefHome(): We can't access the home directory currently, unless
+                                 * it's unlocked with a password. Hence, let's try this again, this time with
+                                 * authentication. */
+                                home_not_active = true;
+                        else if (sd_bus_error_has_name(&error, BUS_ERROR_HOME_LOCKED))
+                                home_locked = true; /* Similar */
+                        else {
+                                r = handle_generic_user_record_error(handle, ur->user_name, secret, r, &error);
+                                if (r == PAM_CONV_ERR) {
+                                        /* Password/PIN prompts will fail in certain environments, for example when
+                                         * we are called from OpenSSH's account or session hooks, or in systemd's
+                                         * per-service PAM logic. In that case, print a friendly message and accept
+                                         * failure. */
+
+                                        if (home_not_active)
+                                                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Home of user %s is currently not active, please log in locally first.", ur->user_name);
+                                        if (home_locked)
+                                                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Home of user %s is currently locked, please unlock locally first.", ur->user_name);
+
+                                        pam_syslog(handle, please_authenticate ? LOG_ERR : LOG_DEBUG, "Failed to prompt for password/prompt.");
+
+                                        return home_not_active || home_locked ? PAM_PERM_DENIED : PAM_CONV_ERR;
+                                }
+                                if (r != PAM_SUCCESS)
+                                        return r;
+                        }
+
+                } else {
+                        int fd;
+
+                        r = sd_bus_message_read(reply, "h", &fd);
+                        if (r < 0)
+                                return pam_bus_log_parse_error(handle, r);
+
+                        acquired_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+                        if (acquired_fd < 0) {
+                                pam_syslog(handle, LOG_ERR, "Failed to duplicate acquired fd: %s", bus_error_message(&error, r));
+                                return PAM_SERVICE_ERR;
+                        }
+
+                        break;
+                }
+
+                if (++n_attempts >= 5) {
+                        (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Too many unsuccessful login attempts for user %s, refusing.", ur->user_name);
+                        pam_syslog(handle, LOG_ERR, "Failed to acquire home for user %s: %s", ur->user_name, bus_error_message(&error, r));
+                        return PAM_MAXTRIES;
+                }
+
+                /* Try again, this time with authentication if we didn't do that before. */
+                do_auth = true;
+        }
+
+        r = pam_set_data(handle, "systemd-home-fd", 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;
+        }
+        TAKE_FD(acquired_fd);
+
+        if (do_auth) {
+                /* 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);
+                if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA))
+                        return r;
+        }
+
+        pam_syslog(handle, LOG_NOTICE, "Home for user %s successfully acquired.", ur->user_name);
+
+        return PAM_SUCCESS;
+}
+
+static int release_home_fd(pam_handle_t *handle) {
+        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)
+                return PAM_NO_MODULE_DATA;
+
+        r = pam_set_data(handle, "systemd-home-fd", NULL, NULL);
+        if (r != PAM_SUCCESS)
+                pam_syslog(handle, LOG_ERR, "Failed to release PAM home reference fd: %s", pam_strerror(handle, r));
+
+        return r;
+}
+
+_public_ PAM_EXTERN int pam_sm_authenticate(
+                pam_handle_t *handle,
+                int flags,
+                int argc, const char **argv) {
+
+        bool debug = false, suspend_please = false;
+
+        if (parse_argv(handle,
+                       argc, argv,
+                       &suspend_please,
+                       &debug) < 0)
+                return PAM_AUTH_ERR;
+
+        if (debug)
+                pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed authenticating");
+
+        return acquire_home(handle, /* please_authenticate= */ true, suspend_please, debug);
+}
+
+_public_ PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) {
+        return PAM_SUCCESS;
+}
+
+_public_ PAM_EXTERN int pam_sm_open_session(
+                pam_handle_t *handle,
+                int flags,
+                int argc, const char **argv) {
+
+        bool debug = false, suspend_please = false;
+        int r;
+
+        if (parse_argv(handle,
+                       argc, argv,
+                       &suspend_please,
+                       &debug) < 0)
+                return PAM_SESSION_ERR;
+
+        if (debug)
+                pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed session start");
+
+        r = acquire_home(handle, /* please_authenticate = */ false, suspend_please, debug);
+        if (r == PAM_USER_UNKNOWN) /* Not managed by us? Don't complain. */
+                return PAM_SUCCESS;
+        if (r != PAM_SUCCESS)
+                return r;
+
+        r = pam_putenv(handle, "SYSTEMD_HOME=1");
+        if (r != PAM_SUCCESS) {
+                pam_syslog(handle, LOG_ERR, "Failed to set PAM environment variable $SYSTEMD_HOME: %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. */
+        (void) pam_release_bus_connection(handle);
+        return PAM_SUCCESS;
+}
+
+_public_ PAM_EXTERN int pam_sm_close_session(
+                pam_handle_t *handle,
+                int flags,
+                int argc, const char **argv) {
+
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+        _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+        const char *username = NULL;
+        bool debug = false;
+        int r;
+
+        if (parse_argv(handle,
+                       argc, argv,
+                       NULL,
+                       &debug) < 0)
+                return PAM_SESSION_ERR;
+
+        if (debug)
+                pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed session end");
+
+        /* 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);
+        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");
+        if (r < 0)
+                return pam_bus_log_create_error(handle, r);
+
+        r = sd_bus_message_append(m, "s", username);
+        if (r < 0)
+                return pam_bus_log_create_error(handle, 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_HOME_BUSY))
+                        pam_syslog(handle, LOG_NOTICE, "Not deactivating home directory of %s, as it is still used.", username);
+                else {
+                        pam_syslog(handle, LOG_ERR, "Failed to release user home: %s", bus_error_message(&error, r));
+                        return PAM_SESSION_ERR;
+                }
+        }
+
+        return PAM_SUCCESS;
+}
+
+_public_ PAM_EXTERN int pam_sm_acct_mgmt(
+                pam_handle_t *handle,
+                int flags,
+                int argc,
+                const char **argv) {
+
+        _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+        bool debug = false, please_suspend = false;
+        usec_t t;
+        int r;
+
+        if (parse_argv(handle,
+                       argc, argv,
+                       &please_suspend,
+                       &debug) < 0)
+                return PAM_AUTH_ERR;
+
+        if (debug)
+                pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed account management");
+
+        r = acquire_home(handle, /* please_authenticate = */ false, please_suspend, debug);
+        if (r == PAM_USER_UNKNOWN)
+                return PAM_SUCCESS; /* we don't have anything to say about users we don't manage */
+        if (r != PAM_SUCCESS)
+                return r;
+
+        r = acquire_user_record(handle, &ur);
+        if (r != PAM_SUCCESS)
+                return r;
+
+        r = user_record_test_blocked(ur);
+        switch (r) {
+
+        case -ESTALE:
+                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is newer than current system time, prohibiting access.");
+                return PAM_ACCT_EXPIRED;
+
+        case -ENOLCK:
+                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is blocked, prohibiting access.");
+                return PAM_ACCT_EXPIRED;
+
+        case -EL2HLT:
+                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is not valid yet, prohibiting access.");
+                return PAM_ACCT_EXPIRED;
+
+        case -EL3HLT:
+                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record is not valid anymore, prohibiting access.");
+                return PAM_ACCT_EXPIRED;
+
+        default:
+                if (r < 0) {
+                        (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record not valid, prohibiting access.");
+                        return PAM_ACCT_EXPIRED;
+                }
+
+                break;
+        }
+
+        t = user_record_ratelimit_next_try(ur);
+        if (t != USEC_INFINITY) {
+                usec_t n = now(CLOCK_REALTIME);
+
+                if (t > n) {
+                        char buf[FORMAT_TIMESPAN_MAX];
+                        (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Too many logins, try again in %s.",
+                                          format_timespan(buf, sizeof(buf), t - n, USEC_PER_SEC));
+
+                        return PAM_MAXTRIES;
+                }
+        }
+
+        r = user_record_test_password_change_required(ur);
+        switch (r) {
+
+        case -EKEYREVOKED:
+                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password change required.");
+                return PAM_NEW_AUTHTOK_REQD;
+
+        case -EOWNERDEAD:
+                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password expired, change requird.");
+                return PAM_NEW_AUTHTOK_REQD;
+
+        case -EKEYREJECTED:
+                /* Strictly speaking this is only about password expiration, and we might want to allow
+                 * authentication via PKCS#11 or so, but let's ignore this fine distinction for now. */
+                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password is expired, but can't change, refusing login.");
+                return PAM_AUTHTOK_EXPIRED;
+
+        case -EKEYEXPIRED:
+                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Password will expire soon, please change.");
+                break;
+
+        case -EROFS:
+                /* All good, just means the password if we wanted to change we couldn't, but we don't need to */
+                break;
+
+        default:
+                if (r < 0) {
+                        (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "User record not valid, prohibiting access.");
+                        return PAM_AUTHTOK_EXPIRED;
+                }
+
+                break;
+        }
+
+        return PAM_SUCCESS;
+}
+
+_public_ PAM_EXTERN int pam_sm_chauthtok(
+                pam_handle_t *handle,
+                int flags,
+                int argc,
+                const char **argv) {
+
+        _cleanup_(user_record_unrefp) UserRecord *ur = NULL, *old_secret = NULL, *new_secret = NULL;
+        const char *old_password = NULL, *new_password = NULL;
+        _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+        unsigned n_attempts = 0;
+        bool debug = false;
+        int r;
+
+        if (parse_argv(handle,
+                       argc, argv,
+                       NULL,
+                       &debug) < 0)
+                return PAM_AUTH_ERR;
+
+        if (debug)
+                pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed account management");
+
+        r = pam_acquire_bus_connection(handle, &bus);
+        if (r != PAM_SUCCESS)
+                return r;
+
+        r = acquire_user_record(handle, &ur);
+        if (r != PAM_SUCCESS)
+                return r;
+
+        /* Start with cached credentials */
+        r = pam_get_item(handle, PAM_OLDAUTHTOK, (const void**) &old_password);
+        if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
+                pam_syslog(handle, LOG_ERR, "Failed to get old password: %s", pam_strerror(handle, r));
+                return r;
+        }
+        r = pam_get_item(handle, PAM_AUTHTOK, (const void**) &new_password);
+        if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
+                pam_syslog(handle, LOG_ERR, "Failed to get cached password: %s", pam_strerror(handle, r));
+                return r;
+        }
+
+        if (isempty(new_password)) {
+                /* No, it's not cached, then let's ask for the password and its verification, and cache
+                 * it. */
+
+                r = pam_get_authtok_noverify(handle, &new_password, "New password: ");
+                if (r != PAM_SUCCESS) {
+                        pam_syslog(handle, LOG_ERR, "Failed to get new password: %s", pam_strerror(handle, r));
+                        return r;
+                }
+                if (isempty(new_password)) {
+                        pam_syslog(handle, LOG_DEBUG, "Password request aborted.");
+                        return PAM_AUTHTOK_ERR;
+                }
+
+                r = pam_get_authtok_verify(handle, &new_password, "new password: "); /* Lower case, since PAM prefixes 'Repeat' */
+                if (r != PAM_SUCCESS) {
+                        pam_syslog(handle, LOG_ERR, "Failed to get password again: %s", pam_strerror(handle, r));
+                        return r;
+                }
+
+                // FIXME: pam_pwquality will ask for the password a third time. It really shouldn't do
+                // that, and instead assume the password was already verified once when it is found to be
+                // cached already. needs to be fixed in pam_pwquality
+        }
+
+        /* Now everything is cached and checked, let's exit from the preliminary check */
+        if (FLAGS_SET(flags, PAM_PRELIM_CHECK))
+                return PAM_SUCCESS;
+
+
+        old_secret = user_record_new();
+        if (!old_secret)
+                return pam_log_oom(handle);
+
+        if (!isempty(old_password)) {
+                r = user_record_set_password(old_secret, STRV_MAKE(old_password), true);
+                if (r < 0) {
+                        pam_syslog(handle, LOG_ERR, "Failed to store old password: %s", strerror_safe(r));
+                        return PAM_SERVICE_ERR;
+                }
+        }
+
+        new_secret = user_record_new();
+        if (!new_secret)
+                return pam_log_oom(handle);
+
+        r = user_record_set_password(new_secret, STRV_MAKE(new_password), true);
+        if (r < 0) {
+                pam_syslog(handle, LOG_ERR, "Failed to store new password: %s", strerror_safe(r));
+                return PAM_SERVICE_ERR;
+        }
+
+        for (;;) {
+                _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");
+                if (r < 0)
+                        return pam_bus_log_create_error(handle, r);
+
+                r = sd_bus_message_append(m, "s", ur->user_name);
+                if (r < 0)
+                        return pam_bus_log_create_error(handle, r);
+
+                r = bus_message_append_secret(m, new_secret);
+                if (r < 0)
+                        return pam_bus_log_create_error(handle, r);
+
+                r = bus_message_append_secret(m, old_secret);
+                if (r < 0)
+                        return pam_bus_log_create_error(handle, r);
+
+                r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
+                if (r < 0) {
+                        r = handle_generic_user_record_error(handle, ur->user_name, old_secret, r, &error);
+                        if (r == PAM_CONV_ERR) {
+                                pam_syslog(handle, LOG_ERR, "Failed to prompt for password/prompt.");
+                                return PAM_CONV_ERR;
+                        }
+                        if (r != PAM_SUCCESS)
+                                return r;
+                } else {
+                        pam_syslog(handle, LOG_NOTICE, "Successfully changed password for user %s.", ur->user_name);
+                        return PAM_SUCCESS;
+                }
+
+                if (++n_attempts >= 5)
+                        break;
+
+                /* Try again */
+        };
+
+        pam_syslog(handle, LOG_NOTICE, "Failed to change password for user %s: %m", ur->user_name);
+        return PAM_MAXTRIES;
+}
diff --git a/src/home/pam_systemd_home.sym b/src/home/pam_systemd_home.sym
new file mode 100644 (file)
index 0000000..daec049
--- /dev/null
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+{
+global:
+        pam_sm_authenticate;
+        pam_sm_setcred;
+        pam_sm_open_session;
+        pam_sm_close_session;
+        pam_sm_acct_mgmt;
+        pam_sm_chauthtok;
+local: *;
+};
diff --git a/src/home/pwquality-util.c b/src/home/pwquality-util.c
new file mode 100644 (file)
index 0000000..f2342b2
--- /dev/null
@@ -0,0 +1,186 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <unistd.h>
+
+#if HAVE_PWQUALITY
+/* pwquality.h uses size_t but doesn't include sys/types.h on its own */
+#include <sys/types.h>
+#include <pwquality.h>
+#endif
+
+#include "bus-common-errors.h"
+#include "home-util.h"
+#include "memory-util.h"
+#include "pwquality-util.h"
+#include "strv.h"
+
+#if HAVE_PWQUALITY
+DEFINE_TRIVIAL_CLEANUP_FUNC(pwquality_settings_t*, pwquality_free_settings);
+
+static void pwquality_maybe_disable_dictionary(
+                pwquality_settings_t *pwq) {
+
+        char buf[PWQ_MAX_ERROR_MESSAGE_LEN];
+        const char *path;
+        int r;
+
+        r = pwquality_get_str_value(pwq, PWQ_SETTING_DICT_PATH, &path);
+        if (r < 0) {
+                log_warning("Failed to read libpwquality dictionary path, ignoring: %s", pwquality_strerror(buf, sizeof(buf), r, NULL));
+                return;
+        }
+
+        // REMOVE THIS AS SOON AS https://github.com/libpwquality/libpwquality/pull/21 IS MERGED AND RELEASED
+        if (isempty(path))
+                path = "/usr/share/cracklib/pw_dict.pwd.gz";
+
+        if (isempty(path)) {
+                log_warning("Weird, no dictionary file configured, ignoring.");
+                return;
+        }
+
+        if (access(path, F_OK) >= 0)
+                return;
+
+        if (errno != ENOENT) {
+                log_warning_errno(errno, "Failed to check if dictionary file %s exists, ignoring: %m", path);
+                return;
+        }
+
+        r = pwquality_set_int_value(pwq, PWQ_SETTING_DICT_CHECK, 0);
+        if (r < 0) {
+                log_warning("Failed to disable libpwquality dictionary check, ignoring: %s", pwquality_strerror(buf, sizeof(buf), r, NULL));
+                return;
+        }
+}
+
+int quality_check_password(
+                UserRecord *hr,
+                UserRecord *secret,
+                sd_bus_error *error) {
+
+        _cleanup_(pwquality_free_settingsp) pwquality_settings_t *pwq = NULL;
+        char buf[PWQ_MAX_ERROR_MESSAGE_LEN], **pp;
+        void *auxerror;
+        int r;
+
+        assert(hr);
+        assert(secret);
+
+        pwq = pwquality_default_settings();
+        if (!pwq)
+                return log_oom();
+
+        r = pwquality_read_config(pwq, NULL, &auxerror);
+        if (r < 0)
+                log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to read libpwquality configuation, ignoring: %s",
+                                  pwquality_strerror(buf, sizeof(buf), r, auxerror));
+
+        pwquality_maybe_disable_dictionary(pwq);
+
+        /* This is a bit more complex than one might think at first. pwquality_check() would like to know the
+         * old password to make security checks. We support arbitrary numbers of passwords however, hence we
+         * call the function once for each combination of old and new password. */
+
+        /* Iterate through all new passwords */
+        STRV_FOREACH(pp, secret->password) {
+                bool called = false;
+                char **old;
+
+                r = test_password_many(hr->hashed_password, *pp);
+                if (r < 0)
+                        return r;
+                if (r == 0) /* This is an old password as it isn't listed in the hashedPassword field, skip it */
+                        continue;
+
+                /* Check this password against all old passwords */
+                STRV_FOREACH(old, secret->password) {
+
+                        if (streq(*pp, *old))
+                                continue;
+
+                        r = test_password_many(hr->hashed_password, *old);
+                        if (r < 0)
+                                return r;
+                        if (r > 0) /* This is a new password, not suitable as old password */
+                                continue;
+
+                        r = pwquality_check(pwq, *pp, *old, hr->user_name, &auxerror);
+                        if (r < 0)
+                                return sd_bus_error_setf(error, BUS_ERROR_LOW_PASSWORD_QUALITY, "Password too weak: %s",
+                                                         pwquality_strerror(buf, sizeof(buf), r, auxerror));
+
+                        called = true;
+                }
+
+                if (called)
+                        continue;
+
+                /* If there are no old passwords, let's call pwquality_check() without any. */
+                r = pwquality_check(pwq, *pp, NULL, hr->user_name, &auxerror);
+                if (r < 0)
+                        return sd_bus_error_setf(error, BUS_ERROR_LOW_PASSWORD_QUALITY, "Password too weak: %s",
+                                                 pwquality_strerror(buf, sizeof(buf), r, auxerror));
+        }
+
+        return 0;
+}
+
+#define N_SUGGESTIONS 6
+
+int suggest_passwords(void) {
+        _cleanup_(pwquality_free_settingsp) pwquality_settings_t *pwq = NULL;
+        _cleanup_strv_free_erase_ char **suggestions = NULL;
+        _cleanup_(erase_and_freep) char *joined = NULL;
+        char buf[PWQ_MAX_ERROR_MESSAGE_LEN];
+        void *auxerror;
+        size_t i;
+        int r;
+
+        pwq = pwquality_default_settings();
+        if (!pwq)
+                return log_oom();
+
+        r = pwquality_read_config(pwq, NULL, &auxerror);
+        if (r < 0)
+                log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to read libpwquality configuation, ignoring: %s",
+                                  pwquality_strerror(buf, sizeof(buf), r, auxerror));
+
+        pwquality_maybe_disable_dictionary(pwq);
+
+        suggestions = new0(char*, N_SUGGESTIONS);
+        if (!suggestions)
+                return log_oom();
+
+        for (i = 0; i < N_SUGGESTIONS; i++) {
+                r = pwquality_generate(pwq, 64, suggestions + i);
+                if (r < 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to generate password, ignoring: %s",
+                                               pwquality_strerror(buf, sizeof(buf), r, NULL));
+        }
+
+        joined = strv_join(suggestions, " ");
+        if (!joined)
+                return log_oom();
+
+        log_info("Password suggestions: %s", joined);
+        return 0;
+}
+
+#else
+
+int quality_check_password(
+                UserRecord *hr,
+                UserRecord *secret,
+                sd_bus_error *error) {
+
+        assert(hr);
+        assert(secret);
+
+        return 0;
+}
+
+int suggest_passwords(void) {
+        return 0;
+}
+#endif
diff --git a/src/home/pwquality-util.h b/src/home/pwquality-util.h
new file mode 100644 (file)
index 0000000..d61c04c
--- /dev/null
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+#include "user-record.h"
+
+int quality_check_password(UserRecord *hr, UserRecord *secret, sd_bus_error *error);
+
+int suggest_passwords(void);
diff --git a/src/home/user-record-sign.c b/src/home/user-record-sign.c
new file mode 100644 (file)
index 0000000..91f8639
--- /dev/null
@@ -0,0 +1,174 @@
+#include <openssl/pem.h>
+
+#include "fd-util.h"
+#include "user-record-sign.h"
+#include "fileio.h"
+
+static int user_record_signable_json(UserRecord *ur, char **ret) {
+        _cleanup_(user_record_unrefp) UserRecord *reduced = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *j = NULL;
+        int r;
+
+        assert(ur);
+        assert(ret);
+
+        r = user_record_clone(ur, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_STRIP_SECRET|USER_RECORD_STRIP_BINDING|USER_RECORD_STRIP_STATUS|USER_RECORD_STRIP_SIGNATURE, &reduced);
+        if (r < 0)
+                return r;
+
+        j = json_variant_ref(reduced->json);
+
+        r = json_variant_normalize(&j);
+        if (r < 0)
+                return r;
+
+        return json_variant_format(j, 0, ret);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(EVP_MD_CTX*, EVP_MD_CTX_free);
+
+int user_record_sign(UserRecord *ur, EVP_PKEY *private_key, UserRecord **ret) {
+        _cleanup_(json_variant_unrefp) JsonVariant *encoded = NULL, *v = NULL;
+        _cleanup_(user_record_unrefp) UserRecord *signed_ur = NULL;
+        _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *md_ctx = NULL;
+        _cleanup_free_ char *text = NULL, *key = NULL;
+        size_t signature_size = 0, key_size = 0;
+        _cleanup_free_ void *signature = NULL;
+        _cleanup_fclose_ FILE *mf = NULL;
+        int r;
+
+        assert(ur);
+        assert(private_key);
+        assert(ret);
+
+        r = user_record_signable_json(ur, &text);
+        if (r < 0)
+                return r;
+
+        md_ctx = EVP_MD_CTX_new();
+        if (!md_ctx)
+                return -ENOMEM;
+
+        if (EVP_DigestSignInit(md_ctx, NULL, NULL, NULL, private_key) <= 0)
+                return -EIO;
+
+        /* Request signature size */
+        if (EVP_DigestSign(md_ctx, NULL, &signature_size, (uint8_t*) text, strlen(text)) <= 0)
+                return -EIO;
+
+        signature = malloc(signature_size);
+        if (!signature)
+                return -ENOMEM;
+
+        if (EVP_DigestSign(md_ctx, signature, &signature_size, (uint8_t*) text, strlen(text)) <= 0)
+                return -EIO;
+
+        mf = open_memstream_unlocked(&key, &key_size);
+        if (!mf)
+                return -ENOMEM;
+
+        if (PEM_write_PUBKEY(mf, private_key) <= 0)
+                return -EIO;
+
+        r = fflush_and_check(mf);
+        if (r < 0)
+                return r;
+
+        r = json_build(&encoded, JSON_BUILD_ARRAY(
+                                       JSON_BUILD_OBJECT(JSON_BUILD_PAIR("data", JSON_BUILD_BASE64(signature, signature_size)),
+                                                         JSON_BUILD_PAIR("key", JSON_BUILD_STRING(key)))));
+        if (r < 0)
+                return r;
+
+        v = json_variant_ref(ur->json);
+
+        r = json_variant_set_field(&v, "signature", encoded);
+        if (r < 0)
+                return r;
+
+        if (DEBUG_LOGGING)
+                json_variant_dump(v, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR_AUTO, NULL, NULL);
+
+        signed_ur = user_record_new();
+        if (!signed_ur)
+                return log_oom();
+
+        r = user_record_load(signed_ur, v, USER_RECORD_LOAD_FULL);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(signed_ur);
+        return 0;
+}
+
+int user_record_verify(UserRecord *ur, EVP_PKEY *public_key) {
+        _cleanup_free_ char *text = NULL;
+        unsigned n_good = 0, n_bad = 0;
+        JsonVariant *array, *e;
+        int r;
+
+        assert(ur);
+        assert(public_key);
+
+        array = json_variant_by_key(ur->json, "signature");
+        if (!array)
+                return USER_RECORD_UNSIGNED;
+
+        if (!json_variant_is_array(array))
+                return -EINVAL;
+
+        if (json_variant_elements(array) == 0)
+                return USER_RECORD_UNSIGNED;
+
+        r = user_record_signable_json(ur, &text);
+        if (r < 0)
+                return r;
+
+        JSON_VARIANT_ARRAY_FOREACH(e, array) {
+                _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *md_ctx = NULL;
+                _cleanup_free_ void *signature = NULL;
+                size_t signature_size = 0;
+                JsonVariant *data;
+
+                if (!json_variant_is_object(e))
+                        return -EINVAL;
+
+                data = json_variant_by_key(e, "data");
+                if (!data)
+                        return -EINVAL;
+
+                r = json_variant_unbase64(data, &signature, &signature_size);
+                if (r < 0)
+                        return r;
+
+                md_ctx = EVP_MD_CTX_new();
+                if (!md_ctx)
+                        return -ENOMEM;
+
+                if (EVP_DigestVerifyInit(md_ctx, NULL, NULL, NULL, public_key) <= 0)
+                        return -EIO;
+
+                if (EVP_DigestVerify(md_ctx, signature, signature_size, (uint8_t*) text, strlen(text)) <= 0) {
+                        n_bad ++;
+                        continue;
+                }
+
+                n_good ++;
+        }
+
+        return n_good > 0 ? (n_bad == 0 ? USER_RECORD_SIGNED_EXCLUSIVE : USER_RECORD_SIGNED) :
+                (n_bad == 0 ? USER_RECORD_UNSIGNED : USER_RECORD_FOREIGN);
+}
+
+int user_record_has_signature(UserRecord *ur) {
+        JsonVariant *array;
+
+        array = json_variant_by_key(ur->json, "signature");
+        if (!array)
+                return false;
+
+        if (!json_variant_is_array(array))
+                return -EINVAL;
+
+        return json_variant_elements(array) > 0;
+}
diff --git a/src/home/user-record-sign.h b/src/home/user-record-sign.h
new file mode 100644 (file)
index 0000000..f045c88
--- /dev/null
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <openssl/evp.h>
+
+#include "user-record.h"
+
+int user_record_sign(UserRecord *ur, EVP_PKEY *private_key, UserRecord **ret);
+
+enum {
+        USER_RECORD_UNSIGNED,           /* user record has no signature */
+        USER_RECORD_SIGNED_EXCLUSIVE,   /* user record has only a signature by our own key */
+        USER_RECORD_SIGNED,             /* user record is signed by us, but by others too */
+        USER_RECORD_FOREIGN,            /* user record is not signed by us, but by others */
+};
+
+int user_record_verify(UserRecord *ur, EVP_PKEY *public_key);
+
+int user_record_has_signature(UserRecord *ur);
diff --git a/src/home/user-record-util.c b/src/home/user-record-util.c
new file mode 100644 (file)
index 0000000..34f9d76
--- /dev/null
@@ -0,0 +1,1227 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "errno-util.h"
+#include "home-util.h"
+#include "id128-util.h"
+#include "libcrypt-util.h"
+#include "mountpoint-util.h"
+#include "path-util.h"
+#include "stat-util.h"
+#include "user-record-util.h"
+#include "user-util.h"
+
+int user_record_synthesize(
+                UserRecord *h,
+                const char *user_name,
+                const char *realm,
+                const char *image_path,
+                UserStorage storage,
+                uid_t uid,
+                gid_t gid) {
+
+        _cleanup_free_ char *hd = NULL, *un = NULL, *ip = NULL, *rr = NULL, *user_name_and_realm = NULL;
+        char smid[SD_ID128_STRING_MAX];
+        sd_id128_t mid;
+        int r;
+
+        assert(h);
+        assert(user_name);
+        assert(image_path);
+        assert(IN_SET(storage, USER_LUKS, USER_SUBVOLUME, USER_FSCRYPT, USER_DIRECTORY));
+        assert(uid_is_valid(uid));
+        assert(gid_is_valid(gid));
+
+        /* Fill in a home record from just a username and an image path. */
+
+        if (h->json)
+                return -EBUSY;
+
+        if (!suitable_user_name(user_name))
+                return -EINVAL;
+
+        if (realm) {
+                r = suitable_realm(realm);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        return -EINVAL;
+        }
+
+        if (!suitable_image_path(image_path))
+                return -EINVAL;
+
+        r = sd_id128_get_machine(&mid);
+        if (r < 0)
+                return r;
+
+        un = strdup(user_name);
+        if (!un)
+                return -ENOMEM;
+
+        if (realm) {
+                rr = strdup(realm);
+                if (!rr)
+                        return -ENOMEM;
+
+                user_name_and_realm = strjoin(user_name, "@", realm);
+                if (!user_name_and_realm)
+                        return -ENOMEM;
+        }
+
+        ip = strdup(image_path);
+        if (!ip)
+                return -ENOMEM;
+
+        hd = path_join("/home/", user_name);
+        if (!hd)
+                return -ENOMEM;
+
+        r = json_build(&h->json,
+                       JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(user_name)),
+                                       JSON_BUILD_PAIR_CONDITION(!!rr, "realm", JSON_BUILD_STRING(realm)),
+                                       JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("regular")),
+                                       JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
+                                                                       JSON_BUILD_PAIR(sd_id128_to_string(mid, smid), JSON_BUILD_OBJECT(
+                                                                                                       JSON_BUILD_PAIR("imagePath", JSON_BUILD_STRING(image_path)),
+                                                                                                       JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING(hd)),
+                                                                                                       JSON_BUILD_PAIR("storage", JSON_BUILD_STRING(user_storage_to_string(storage))),
+                                                                                                       JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid)),
+                                                                                                       JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid))))))));
+        if (r < 0)
+                return r;
+
+        free_and_replace(h->user_name, un);
+        free_and_replace(h->realm, rr);
+        free_and_replace(h->user_name_and_realm_auto, user_name_and_realm);
+        free_and_replace(h->image_path, ip);
+        free_and_replace(h->home_directory, hd);
+        h->storage = storage;
+        h->uid = uid;
+
+        h->mask = USER_RECORD_REGULAR|USER_RECORD_BINDING;
+        return 0;
+}
+
+int group_record_synthesize(GroupRecord *g, UserRecord *h) {
+        _cleanup_free_ char *un = NULL, *rr = NULL, *group_name_and_realm = NULL;
+        char smid[SD_ID128_STRING_MAX];
+        sd_id128_t mid;
+        int r;
+
+        assert(g);
+        assert(h);
+
+        if (g->json)
+                return -EBUSY;
+
+        r = sd_id128_get_machine(&mid);
+        if (r < 0)
+                return r;
+
+        un = strdup(h->user_name);
+        if (!un)
+                return -ENOMEM;
+
+        if (h->realm) {
+                rr = strdup(h->realm);
+                if (!rr)
+                        return -ENOMEM;
+
+                group_name_and_realm = strjoin(un, "@", rr);
+                if (!group_name_and_realm)
+                        return -ENOMEM;
+        }
+
+        r = json_build(&g->json,
+                       JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(un)),
+                                       JSON_BUILD_PAIR_CONDITION(!!rr, "realm", JSON_BUILD_STRING(rr)),
+                                       JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
+                                                                       JSON_BUILD_PAIR(sd_id128_to_string(mid, smid), JSON_BUILD_OBJECT(
+                                                                                                       JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(user_record_gid(h))))))),
+                                       JSON_BUILD_PAIR_CONDITION(h->disposition >= 0, "disposition", JSON_BUILD_STRING(user_disposition_to_string(user_record_disposition(h)))),
+                                       JSON_BUILD_PAIR("status", JSON_BUILD_OBJECT(
+                                                                       JSON_BUILD_PAIR(sd_id128_to_string(mid, smid), JSON_BUILD_OBJECT(
+                                                                                                       JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.Home"))))))));
+        if (r < 0)
+                return r;
+
+        free_and_replace(g->group_name, un);
+        free_and_replace(g->realm, rr);
+        free_and_replace(g->group_name_and_realm_auto, group_name_and_realm);
+        g->gid = user_record_gid(h);
+        g->disposition = h->disposition;
+
+        g->mask = USER_RECORD_REGULAR|USER_RECORD_BINDING;
+        return 0;
+}
+
+int user_record_reconcile(
+                UserRecord *host,
+                UserRecord *embedded,
+                UserReconcileMode mode,
+                UserRecord **ret) {
+
+        int r, result;
+
+        /* Reconciles the identity record stored on the host with the one embedded in a $HOME
+         * directory. Returns the following error codes:
+         *
+         *     -EINVAL: one of the records not valid
+         *     -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
+         * binding or the host record. In both cases the secret data is stripped. */
+
+        assert(host);
+        assert(embedded);
+
+        /* Make sure both records are initialized */
+        if (!host->json || !embedded->json)
+                return -EINVAL;
+
+        /* Ensure these records actually contain user data */
+        if (!(embedded->mask & host->mask & USER_RECORD_REGULAR))
+                return -EINVAL;
+
+        /* Make sure the user name and realm matches */
+        if (!user_record_compatible(host, embedded))
+                return -EREMCHG;
+
+        /* Embedded identities may not contain secrets or binding info*/
+        if ((embedded->mask & (USER_RECORD_SECRET|USER_RECORD_BINDING)) != 0)
+                return -EINVAL;
+
+        /* The embedded record checked out, let's now figure out which of the two identities we'll consider
+         * in effect from now on. We do this by checking the last change timestamp, and in doubt always let
+         * the embedded data win. */
+        if (host->last_change_usec != UINT64_MAX &&
+            (embedded->last_change_usec == UINT64_MAX || host->last_change_usec > embedded->last_change_usec))
+
+                /* The host version is definitely newer, either because it has a version at all and the
+                 * embedded version doesn't or because it is numerically newer. */
+                result = USER_RECONCILE_HOST_WON;
+
+        else if (host->last_change_usec == embedded->last_change_usec) {
+
+                /* The nominal version number of the host and the embedded identity is the same. If so, let's
+                 * verify that, and tell the caller if we are ignoring embedded data. */
+
+                r = user_record_masked_equal(host, embedded, USER_RECORD_REGULAR|USER_RECORD_PRIVILEGED|USER_RECORD_PER_MACHINE);
+                if (r < 0)
+                        return r;
+                if (r > 0) {
+                        if (mode == USER_RECONCILE_REQUIRE_NEWER)
+                                return -ESTALE;
+
+                        result = USER_RECONCILE_IDENTICAL;
+                } else
+                        result = USER_RECONCILE_HOST_WON;
+        } else {
+                _cleanup_(json_variant_unrefp) JsonVariant *extended = NULL;
+                _cleanup_(user_record_unrefp) UserRecord *merged = NULL;
+                JsonVariant *e;
+
+                /* The embedded version is newer */
+
+                if (mode == USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL)
+                        return -ESTALE;
+
+                /* Copy in the binding data */
+                extended = json_variant_ref(embedded->json);
+
+                e = json_variant_by_key(host->json, "binding");
+                if (e) {
+                        r = json_variant_set_field(&extended, "binding", e);
+                        if (r < 0)
+                                return r;
+                }
+
+                merged = user_record_new();
+                if (!merged)
+                        return -ENOMEM;
+
+                r = user_record_load(merged, extended, USER_RECORD_LOAD_MASK_SECRET);
+                if (r < 0)
+                        return r;
+
+                *ret = TAKE_PTR(merged);
+                return USER_RECONCILE_EMBEDDED_WON; /* update */
+        }
+
+        /* Strip out secrets */
+        r = user_record_clone(host, USER_RECORD_LOAD_MASK_SECRET, ret);
+        if (r < 0)
+                return r;
+
+        return result;
+}
+
+int user_record_add_binding(
+                UserRecord *h,
+                UserStorage storage,
+                const char *image_path,
+                sd_id128_t partition_uuid,
+                sd_id128_t luks_uuid,
+                sd_id128_t fs_uuid,
+                const char *luks_cipher,
+                const char *luks_cipher_mode,
+                uint64_t luks_volume_key_size,
+                const char *file_system_type,
+                const char *home_directory,
+                uid_t uid,
+                gid_t gid) {
+
+        _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;
+        sd_id128_t mid;
+        int r;
+
+        assert(h);
+
+        if (!h->json)
+                return -EUNATCH;
+
+        r = sd_id128_get_machine(&mid);
+        if (r < 0)
+                return r;
+        sd_id128_to_string(mid, smid);
+
+        if (image_path) {
+                ip = strdup(image_path);
+                if (!ip)
+                        return -ENOMEM;
+        }
+
+        if (home_directory) {
+                hd = strdup(home_directory);
+                if (!hd)
+                        return -ENOMEM;
+        }
+
+        r = json_build(&new_binding_entry,
+                       JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR_CONDITION(!!image_path, "imagePath", JSON_BUILD_STRING(image_path)),
+                                       JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(partition_uuid), "partitionUuid", JSON_BUILD_STRING(id128_to_uuid_string(partition_uuid, partition_uuids))),
+                                       JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(luks_uuid), "luksUuid", JSON_BUILD_STRING(id128_to_uuid_string(luks_uuid, luks_uuids))),
+                                       JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(fs_uuid), "fileSystemUuid", JSON_BUILD_STRING(id128_to_uuid_string(fs_uuid, fs_uuids))),
+                                       JSON_BUILD_PAIR_CONDITION(!!luks_cipher, "luksCipher", JSON_BUILD_STRING(luks_cipher)),
+                                       JSON_BUILD_PAIR_CONDITION(!!luks_cipher_mode, "luksCipherMode", JSON_BUILD_STRING(luks_cipher_mode)),
+                                       JSON_BUILD_PAIR_CONDITION(luks_volume_key_size != UINT64_MAX, "luksVolumeKeySize", JSON_BUILD_UNSIGNED(luks_volume_key_size)),
+                                       JSON_BUILD_PAIR_CONDITION(!!file_system_type, "fileSystemType", JSON_BUILD_STRING(file_system_type)),
+                                       JSON_BUILD_PAIR_CONDITION(!!home_directory, "homeDirectory", JSON_BUILD_STRING(home_directory)),
+                                       JSON_BUILD_PAIR_CONDITION(uid_is_valid(uid), "uid", JSON_BUILD_UNSIGNED(uid)),
+                                       JSON_BUILD_PAIR_CONDITION(gid_is_valid(gid), "gid", JSON_BUILD_UNSIGNED(gid)),
+                                       JSON_BUILD_PAIR_CONDITION(storage >= 0, "storage", JSON_BUILD_STRING(user_storage_to_string(storage)))));
+        if (r < 0)
+                return r;
+
+        binding = json_variant_ref(json_variant_by_key(h->json, "binding"));
+        if (binding) {
+                _cleanup_(json_variant_unrefp) JsonVariant *be = NULL;
+
+                /* Merge the new entry with an old one, if that exists */
+                be = json_variant_ref(json_variant_by_key(binding, smid));
+                if (be) {
+                        r = json_variant_merge(&be, new_binding_entry);
+                        if (r < 0)
+                                return r;
+
+                        json_variant_unref(new_binding_entry);
+                        new_binding_entry = TAKE_PTR(be);
+                }
+        }
+
+        r = json_variant_set_field(&binding, smid, new_binding_entry);
+        if (r < 0)
+                return r;
+
+        r = json_variant_set_field(&h->json, "binding", binding);
+        if (r < 0)
+                return r;
+
+        if (storage >= 0)
+                h->storage = storage;
+
+        if (ip)
+                free_and_replace(h->image_path, ip);
+
+        if (!sd_id128_is_null(partition_uuid))
+                h->partition_uuid = partition_uuid;
+
+        if (!sd_id128_is_null(luks_uuid))
+                h->luks_uuid = luks_uuid;
+
+        if (!sd_id128_is_null(fs_uuid))
+                h->file_system_uuid = fs_uuid;
+
+        if (hd)
+                free_and_replace(h->home_directory, hd);
+
+        if (uid_is_valid(uid))
+                h->uid = uid;
+
+        h->mask |= USER_RECORD_BINDING;
+        return 1;
+}
+
+int user_record_test_home_directory(UserRecord *h) {
+        const char *hd;
+        int r;
+
+        assert(h);
+
+        /* Returns one of USER_TEST_ABSENT, USER_TEST_MOUNTED, USER_TEST_EXISTS on success */
+
+        hd = user_record_home_directory(h);
+        if (!hd)
+                return -ENXIO;
+
+        r = is_dir(hd, false);
+        if (r == -ENOENT)
+                return USER_TEST_ABSENT;
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -ENOTDIR;
+
+        r = path_is_mount_point(hd, NULL, 0);
+        if (r < 0)
+                return r;
+        if (r > 0)
+                return USER_TEST_MOUNTED;
+
+        /* If the image path and the home directory are identical, then it's OK if the directory is
+         * populated. */
+        if (IN_SET(user_record_storage(h), USER_CLASSIC, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT)) {
+                const char *ip;
+
+                ip = user_record_image_path(h);
+                if (ip && path_equal(ip, hd))
+                        return USER_TEST_EXISTS;
+        }
+
+        /* Otherwise it's not OK */
+        r = dir_is_empty(hd);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -EBUSY;
+
+        return USER_TEST_EXISTS;
+}
+
+int user_record_test_home_directory_and_warn(UserRecord *h) {
+        int r;
+
+        assert(h);
+
+        r = user_record_test_home_directory(h);
+        if (r == -ENXIO)
+                return log_error_errno(r, "User record lacks home directory, refusing.");
+        if (r == -ENOTDIR)
+                return log_error_errno(r, "Home directory %s is not a directory, refusing.", user_record_home_directory(h));
+        if (r == -EBUSY)
+                return log_error_errno(r, "Home directory %s exists, is not mounted but populated, refusing.", user_record_home_directory(h));
+        if (r < 0)
+                return log_error_errno(r, "Failed to test whether the home directory %s exists: %m", user_record_home_directory(h));
+
+        return r;
+}
+
+int user_record_test_image_path(UserRecord *h) {
+        const char *ip;
+        struct stat st;
+
+        assert(h);
+
+        if (user_record_storage(h) == USER_CIFS)
+                return USER_TEST_UNDEFINED;
+
+        ip = user_record_image_path(h);
+        if (!ip)
+                return -ENXIO;
+
+        if (stat(ip, &st) < 0) {
+                if (errno == ENOENT)
+                        return USER_TEST_ABSENT;
+
+                return -errno;
+        }
+
+        switch (user_record_storage(h)) {
+
+        case USER_LUKS:
+                if (S_ISREG(st.st_mode))
+                        return USER_TEST_EXISTS;
+                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
+                         * 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. */
+
+                        if (PATH_STARTSWITH_SET(ip,
+                                                "/dev/disk/by-uuid/",
+                                                "/dev/disk/by-partuuid/",
+                                                "/dev/disk/by-partlabel/",
+                                                "/dev/disk/by-label/"))
+                                return USER_TEST_EXISTS;
+
+                        return USER_TEST_MAYBE;
+                }
+
+                return -EBADFD;
+
+        case USER_CLASSIC:
+        case USER_DIRECTORY:
+        case USER_SUBVOLUME:
+        case USER_FSCRYPT:
+                if (S_ISDIR(st.st_mode))
+                        return USER_TEST_EXISTS;
+
+                return -ENOTDIR;
+
+        default:
+                assert_not_reached("Unexpected record type");
+        }
+}
+
+int user_record_test_image_path_and_warn(UserRecord *h) {
+        int r;
+
+        assert(h);
+
+        r = user_record_test_image_path(h);
+        if (r == -ENXIO)
+                return log_error_errno(r, "User record lacks image path, refusing.");
+        if (r == -EBADFD)
+                return log_error_errno(r, "Image path %s is not a regular file or block device, refusing.", user_record_image_path(h));
+        if (r == -ENOTDIR)
+                return log_error_errno(r, "Image path %s is not a directory, refusing.", user_record_image_path(h));
+        if (r < 0)
+                return log_error_errno(r, "Failed to test whether image path %s exists: %m", user_record_image_path(h));
+
+        return r;
+}
+
+int user_record_test_secret(UserRecord *h, UserRecord *secret) {
+        char **i;
+        int r;
+
+        assert(h);
+
+        /* Checks whether any of the specified passwords matches any of the hashed passwords of the entry */
+
+        if (strv_isempty(h->hashed_password))
+                return -ENXIO;
+
+        STRV_FOREACH(i, secret->password) {
+                r = test_password_many(h->hashed_password, *i);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        return 0;
+        }
+
+        return -ENOKEY;
+}
+
+int user_record_set_disk_size(UserRecord *h, uint64_t disk_size) {
+        _cleanup_(json_variant_unrefp) JsonVariant *new_per_machine = NULL, *midv = NULL, *midav = NULL, *ne = NULL;
+        _cleanup_free_ JsonVariant **array = NULL;
+        char smid[SD_ID128_STRING_MAX];
+        size_t idx = SIZE_MAX, n;
+        JsonVariant *per_machine;
+        sd_id128_t mid;
+        int r;
+
+        assert(h);
+
+        if (!h->json)
+                return -EUNATCH;
+
+        if (disk_size < USER_DISK_SIZE_MIN || disk_size > USER_DISK_SIZE_MAX)
+                return -ERANGE;
+
+        r = sd_id128_get_machine(&mid);
+        if (r < 0)
+                return r;
+
+        sd_id128_to_string(mid, smid);
+
+        r = json_variant_new_string(&midv, smid);
+        if (r < 0)
+                return r;
+
+        r = json_variant_new_array(&midav, (JsonVariant*[]) { midv }, 1);
+        if (r < 0)
+                return r;
+
+        per_machine = json_variant_by_key(h->json, "perMachine");
+        if (per_machine) {
+                size_t i;
+
+                if (!json_variant_is_array(per_machine))
+                        return -EINVAL;
+
+                n = json_variant_elements(per_machine);
+
+                array = new(JsonVariant*, n + 1);
+                if (!array)
+                        return -ENOMEM;
+
+                for (i = 0; i < n; i++) {
+                        JsonVariant *m;
+
+                        array[i] = json_variant_by_index(per_machine, i);
+
+                        if (!json_variant_is_object(array[i]))
+                                return -EINVAL;
+
+                        m = json_variant_by_key(array[i], "matchMachineId");
+                        if (!m) {
+                                /* No machineId field? Let's ignore this, but invalidate what we found so far */
+                                idx = SIZE_MAX;
+                                continue;
+                        }
+
+                        if (json_variant_equal(m, midv) ||
+                            json_variant_equal(m, midav)) {
+                                /* Matches exactly what we are looking for. Let's use this */
+                                idx = i;
+                                continue;
+                        }
+
+                        r = per_machine_id_match(m, JSON_PERMISSIVE);
+                        if (r < 0)
+                                return r;
+                        if (r > 0)
+                                /* Also matches what we are looking for, but with a broader match. In this
+                                 * case let's ignore this entry, and add a new specific one to the end. */
+                                idx = SIZE_MAX;
+                }
+
+                if (idx == SIZE_MAX)
+                        idx = n++; /* Nothing suitable found, place new entry at end */
+                else
+                        ne = json_variant_ref(array[idx]);
+
+        } else {
+                array = new(JsonVariant*, 1);
+                if (!array)
+                        return -ENOMEM;
+
+                idx = 0;
+                n = 1;
+        }
+
+        if (!ne) {
+                r = json_variant_set_field(&ne, "matchMachineId", midav);
+                if (r < 0)
+                        return r;
+        }
+
+        r = json_variant_set_field_unsigned(&ne, "diskSize", disk_size);
+        if (r < 0)
+                return r;
+
+        assert(idx < n);
+        array[idx] = ne;
+
+        r = json_variant_new_array(&new_per_machine, array, n);
+        if (r < 0)
+                return r;
+
+        r = json_variant_set_field(&h->json, "perMachine", new_per_machine);
+        if (r < 0)
+                return r;
+
+        h->disk_size = disk_size;
+        h->mask |= USER_RECORD_PER_MACHINE;
+        return 0;
+}
+
+int user_record_update_last_changed(UserRecord *h, bool with_password) {
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        usec_t n;
+        int r;
+
+        assert(h);
+
+        if (!h->json)
+                return -EUNATCH;
+
+        n = now(CLOCK_REALTIME);
+
+        /* refuse downgrading */
+        if (h->last_change_usec != UINT64_MAX && h->last_change_usec >= n)
+                return -ECHRNG;
+        if (h->last_password_change_usec != UINT64_MAX && h->last_password_change_usec >= n)
+                return -ECHRNG;
+
+        v = json_variant_ref(h->json);
+
+        r = json_variant_set_field_unsigned(&v, "lastChangeUSec", n);
+        if (r < 0)
+                return r;
+
+        if (with_password) {
+                r = json_variant_set_field_unsigned(&v, "lastPasswordChangeUSec", n);
+                if (r < 0)
+                        return r;
+
+                h->last_password_change_usec = n;
+        }
+
+        h->last_change_usec = n;
+
+        json_variant_unref(h->json);
+        h->json = TAKE_PTR(v);
+
+        h->mask |= USER_RECORD_REGULAR;
+        return 0;
+}
+
+int user_record_make_hashed_password(UserRecord *h, char **secret, bool extend) {
+        _cleanup_(json_variant_unrefp) JsonVariant *priv = NULL;
+        _cleanup_strv_free_ char **np = NULL;
+        char **i;
+        int r;
+
+        assert(h);
+        assert(secret);
+
+        /* Initializes the hashed password list from the specified plaintext passwords */
+
+        if (extend) {
+                np = strv_copy(h->hashed_password);
+                if (!np)
+                        return -ENOMEM;
+
+                strv_uniq(np);
+        }
+
+        STRV_FOREACH(i, secret) {
+                _cleanup_free_ char *salt = NULL;
+                struct crypt_data cd = {};
+                char *k;
+
+                r = make_salt(&salt);
+                if (r < 0)
+                        return r;
+
+                errno = 0;
+                k = crypt_r(*i, salt, &cd);
+                if (!k)
+                        return errno_or_else(EINVAL);
+
+                r = strv_extend(&np, k);
+                if (r < 0)
+                        return r;
+        }
+
+        priv = json_variant_ref(json_variant_by_key(h->json, "privileged"));
+
+        if (strv_isempty(np))
+                r = json_variant_filter(&priv, STRV_MAKE("hashedPassword"));
+        else {
+                _cleanup_(json_variant_unrefp) JsonVariant *new_array = NULL;
+
+                r = json_variant_new_array_strv(&new_array, np);
+                if (r < 0)
+                        return r;
+
+                r = json_variant_set_field(&priv, "hashedPassword", new_array);
+                if (r < 0)
+                        return r;
+        }
+
+        r = json_variant_set_field(&h->json, "privileged", priv);
+        if (r < 0)
+                return r;
+
+        strv_free_and_replace(h->hashed_password, np);
+
+        SET_FLAG(h->mask, USER_RECORD_PRIVILEGED, !json_variant_is_blank_object(priv));
+        return 0;
+}
+
+int user_record_set_hashed_password(UserRecord *h, char **hashed_password) {
+        _cleanup_(json_variant_unrefp) JsonVariant *priv = NULL;
+        _cleanup_strv_free_ char **copy = NULL;
+        int r;
+
+        assert(h);
+
+        priv = json_variant_ref(json_variant_by_key(h->json, "privileged"));
+
+        if (strv_isempty(hashed_password))
+                r = json_variant_filter(&priv, STRV_MAKE("hashedPassword"));
+        else {
+                _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
+
+                copy = strv_copy(hashed_password);
+                if (!copy)
+                        return -ENOMEM;
+
+                strv_uniq(copy);
+
+                r = json_variant_new_array_strv(&array, copy);
+                if (r < 0)
+                        return r;
+
+                r = json_variant_set_field(&priv, "hashedPassword", array);
+        }
+        if (r < 0)
+                return r;
+
+        r = json_variant_set_field(&h->json, "privileged", priv);
+        if (r < 0)
+                return r;
+
+        strv_free_and_replace(h->hashed_password, copy);
+
+        SET_FLAG(h->mask, USER_RECORD_PRIVILEGED, !json_variant_is_blank_object(priv));
+        return 0;
+}
+
+int user_record_set_password(UserRecord *h, char **password, bool prepend) {
+        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+        _cleanup_(strv_free_erasep) char **e = NULL;
+        int r;
+
+        assert(h);
+
+        if (prepend) {
+                e = strv_copy(password);
+                if (!e)
+                        return -ENOMEM;
+
+                r = strv_extend_strv(&e, h->password, true);
+                if (r < 0)
+                        return r;
+
+                strv_uniq(e);
+
+                if (strv_equal(h->password, e))
+                        return 0;
+
+        } else {
+                if (strv_equal(h->password, password))
+                        return 0;
+
+                e = strv_copy(password);
+                if (!e)
+                        return -ENOMEM;
+
+                strv_uniq(e);
+        }
+
+        w = json_variant_ref(json_variant_by_key(h->json, "secret"));
+
+        if (strv_isempty(e))
+                r = json_variant_filter(&w, STRV_MAKE("password"));
+        else {
+                _cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
+
+                r = json_variant_new_array_strv(&l, e);
+                if (r < 0)
+                        return r;
+
+                json_variant_sensitive(l);
+
+                r = json_variant_set_field(&w, "password", l);
+        }
+        if (r < 0)
+                return r;
+
+        r = json_variant_set_field(&h->json, "secret", w);
+        if (r < 0)
+                return r;
+
+        strv_free_and_replace(h->password, e);
+
+        SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
+        return 0;
+}
+
+int user_record_set_pkcs11_pin(UserRecord *h, char **pin, bool prepend) {
+        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+        _cleanup_(strv_free_erasep) char **e = NULL;
+        int r;
+
+        assert(h);
+
+        if (prepend) {
+                e = strv_copy(pin);
+                if (!e)
+                        return -ENOMEM;
+
+                r = strv_extend_strv(&e, h->pkcs11_pin, true);
+                if (r < 0)
+                        return r;
+
+                strv_uniq(e);
+
+                if (strv_equal(h->pkcs11_pin, e))
+                        return 0;
+
+        } else {
+                if (strv_equal(h->pkcs11_pin, pin))
+                        return 0;
+
+                e = strv_copy(pin);
+                if (!e)
+                        return -ENOMEM;
+
+                strv_uniq(e);
+        }
+
+        w = json_variant_ref(json_variant_by_key(h->json, "secret"));
+
+        if (strv_isempty(e))
+                r = json_variant_filter(&w, STRV_MAKE("pkcs11Pin"));
+        else {
+                _cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
+
+                r = json_variant_new_array_strv(&l, e);
+                if (r < 0)
+                        return r;
+
+                json_variant_sensitive(l);
+
+                r = json_variant_set_field(&w, "pkcs11Pin", l);
+        }
+        if (r < 0)
+                return r;
+
+        r = json_variant_set_field(&h->json, "secret", w);
+        if (r < 0)
+                return r;
+
+        strv_free_and_replace(h->pkcs11_pin, e);
+
+        SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
+        return 0;
+}
+
+int user_record_set_pkcs11_protected_authentication_path_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("pkcs11ProtectedAuthenticationPathPermitted"));
+        else
+                r = json_variant_set_field_boolean(&w, "pkcs11ProtectedAuthenticationPathPermitted", 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->pkcs11_protected_authentication_path_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;
+
+        JSON_VARIANT_OBJECT_FOREACH(k, e, v)
+                if (!STR_IN_SET(k, "matchMachineId", "matchHostname"))
+                        return false;
+
+        return true;
+}
+
+int user_record_set_password_change_now(UserRecord *h, int b) {
+        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+        JsonVariant *per_machine;
+        int r;
+
+        assert(h);
+
+        w = json_variant_ref(h->json);
+
+        if (b < 0)
+                r = json_variant_filter(&w, STRV_MAKE("passwordChangeNow"));
+        else
+                r = json_variant_set_field_boolean(&w, "passwordChangeNow", b);
+        if (r < 0)
+                return r;
+
+        /* Also drop the field from all perMachine entries */
+        per_machine = json_variant_by_key(w, "perMachine");
+        if (per_machine) {
+                _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
+                JsonVariant *e;
+
+                JSON_VARIANT_ARRAY_FOREACH(e, per_machine) {
+                        _cleanup_(json_variant_unrefp) JsonVariant *z = NULL;
+
+                        if (!json_variant_is_object(e))
+                                return -EINVAL;
+
+                        z = json_variant_ref(e);
+
+                        r = json_variant_filter(&z, STRV_MAKE("passwordChangeNow"));
+                        if (r < 0)
+                                return r;
+
+                        if (per_machine_entry_empty(z))
+                                continue;
+
+                        r = json_variant_append_array(&array, z);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (json_variant_is_blank_array(array))
+                        r = json_variant_filter(&w, STRV_MAKE("perMachine"));
+                else
+                        r = json_variant_set_field(&w, "perMachine", array);
+                if (r < 0)
+                        return r;
+
+                SET_FLAG(h->mask, USER_RECORD_PER_MACHINE, !json_variant_is_blank_array(array));
+        }
+
+        json_variant_unref(h->json);
+        h->json = TAKE_PTR(w);
+
+        h->password_change_now = b;
+
+        return 0;
+}
+
+int user_record_merge_secret(UserRecord *h, UserRecord *secret) {
+        int r;
+
+        assert(h);
+
+        /* Merges the secrets from 'secret' into 'h'. */
+
+        r = user_record_set_password(h, secret->password, true);
+        if (r < 0)
+                return r;
+
+        r = user_record_set_pkcs11_pin(h, secret->pkcs11_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);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+int user_record_good_authentication(UserRecord *h) {
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
+        char buf[SD_ID128_STRING_MAX];
+        uint64_t counter, usec;
+        sd_id128_t mid;
+        int r;
+
+        assert(h);
+
+        switch (h->good_authentication_counter) {
+        case UINT64_MAX:
+                counter = 1;
+                break;
+        case UINT64_MAX-1:
+                counter = h->good_authentication_counter; /* saturate */
+                break;
+        default:
+                counter = h->good_authentication_counter + 1;
+                break;
+        }
+
+        usec = now(CLOCK_REALTIME);
+
+        r = sd_id128_get_machine(&mid);
+        if (r < 0)
+                return r;
+
+        v = json_variant_ref(h->json);
+        w = json_variant_ref(json_variant_by_key(v, "status"));
+        z = json_variant_ref(json_variant_by_key(w, sd_id128_to_string(mid, buf)));
+
+        r = json_variant_set_field_unsigned(&z, "goodAuthenticationCounter", counter);
+        if (r < 0)
+                return r;
+
+        r = json_variant_set_field_unsigned(&z, "lastGoodAuthenticationUSec", usec);
+        if (r < 0)
+                return r;
+
+        r = json_variant_set_field(&w, buf, z);
+        if (r < 0)
+                return r;
+
+        r = json_variant_set_field(&v, "status", w);
+        if (r < 0)
+                return r;
+
+        json_variant_unref(h->json);
+        h->json = TAKE_PTR(v);
+
+        h->good_authentication_counter = counter;
+        h->last_good_authentication_usec = usec;
+
+        h->mask |= USER_RECORD_STATUS;
+        return 0;
+}
+
+int user_record_bad_authentication(UserRecord *h) {
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
+        char buf[SD_ID128_STRING_MAX];
+        uint64_t counter, usec;
+        sd_id128_t mid;
+        int r;
+
+        assert(h);
+
+        switch (h->bad_authentication_counter) {
+        case UINT64_MAX:
+                counter = 1;
+                break;
+        case UINT64_MAX-1:
+                counter = h->bad_authentication_counter; /* saturate */
+                break;
+        default:
+                counter = h->bad_authentication_counter + 1;
+                break;
+        }
+
+        usec = now(CLOCK_REALTIME);
+
+        r = sd_id128_get_machine(&mid);
+        if (r < 0)
+                return r;
+
+        v = json_variant_ref(h->json);
+        w = json_variant_ref(json_variant_by_key(v, "status"));
+        z = json_variant_ref(json_variant_by_key(w, sd_id128_to_string(mid, buf)));
+
+        r = json_variant_set_field_unsigned(&z, "badAuthenticationCounter", counter);
+        if (r < 0)
+                return r;
+
+        r = json_variant_set_field_unsigned(&z, "lastBadAuthenticationUSec", usec);
+        if (r < 0)
+                return r;
+
+        r = json_variant_set_field(&w, buf, z);
+        if (r < 0)
+                return r;
+
+        r = json_variant_set_field(&v, "status", w);
+        if (r < 0)
+                return r;
+
+        json_variant_unref(h->json);
+        h->json = TAKE_PTR(v);
+
+        h->bad_authentication_counter = counter;
+        h->last_bad_authentication_usec = usec;
+
+        h->mask |= USER_RECORD_STATUS;
+        return 0;
+}
+
+int user_record_ratelimit(UserRecord *h) {
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
+        usec_t usec, new_ratelimit_begin_usec, new_ratelimit_count;
+        char buf[SD_ID128_STRING_MAX];
+        sd_id128_t mid;
+        int r;
+
+        assert(h);
+
+        usec = now(CLOCK_REALTIME);
+
+        if (h->ratelimit_begin_usec != UINT64_MAX && h->ratelimit_begin_usec > usec)
+                /* Hmm, time is running backwards? Say no! */
+                return 0;
+        else if (h->ratelimit_begin_usec == UINT64_MAX ||
+                 usec_add(h->ratelimit_begin_usec, user_record_ratelimit_interval_usec(h)) <= usec) {
+                /* Fresh start */
+                new_ratelimit_begin_usec = usec;
+                new_ratelimit_count = 1;
+        } else if (h->ratelimit_count < user_record_ratelimit_burst(h)) {
+                /* Count up */
+                new_ratelimit_begin_usec = h->ratelimit_begin_usec;
+                new_ratelimit_count = h->ratelimit_count + 1;
+        } else
+                /* Limit hit */
+                return 0;
+
+        r = sd_id128_get_machine(&mid);
+        if (r < 0)
+                return r;
+
+        v = json_variant_ref(h->json);
+        w = json_variant_ref(json_variant_by_key(v, "status"));
+        z = json_variant_ref(json_variant_by_key(w, sd_id128_to_string(mid, buf)));
+
+        r = json_variant_set_field_unsigned(&z, "rateLimitBeginUSec", new_ratelimit_begin_usec);
+        if (r < 0)
+                return r;
+
+        r = json_variant_set_field_unsigned(&z, "rateLimitCount", new_ratelimit_count);
+        if (r < 0)
+                return r;
+
+        r = json_variant_set_field(&w, buf, z);
+        if (r < 0)
+                return r;
+
+        r = json_variant_set_field(&v, "status", w);
+        if (r < 0)
+                return r;
+
+        json_variant_unref(h->json);
+        h->json = TAKE_PTR(v);
+
+        h->ratelimit_begin_usec = new_ratelimit_begin_usec;
+        h->ratelimit_count = new_ratelimit_count;
+
+        h->mask |= USER_RECORD_STATUS;
+        return 1;
+}
+
+int user_record_is_supported(UserRecord *hr, sd_bus_error *error) {
+        assert(hr);
+
+        if (hr->disposition >= 0 && hr->disposition != USER_REGULAR)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot manage anything but regular users.");
+
+        if (hr->storage >= 0 && !IN_SET(hr->storage, USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User record has storage type this service cannot manage.");
+
+        if (gid_is_valid(hr->gid) && hr->uid != (uid_t) hr->gid)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User record has to have matching UID/GID fields.");
+
+        if (hr->service && !streq(hr->service, "io.systemd.Home"))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not accepted with service not matching io.systemd.Home.");
+
+        return 0;
+}
diff --git a/src/home/user-record-util.h b/src/home/user-record-util.h
new file mode 100644 (file)
index 0000000..6afc8df
--- /dev/null
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+#include "user-record.h"
+#include "group-record.h"
+
+int user_record_synthesize(UserRecord *h, const char *user_name, const char *realm, const char *image_path, UserStorage storage, uid_t uid, gid_t gid);
+int group_record_synthesize(GroupRecord *g, UserRecord *u);
+
+typedef enum UserReconcileMode {
+        USER_RECONCILE_ANY,
+        USER_RECONCILE_REQUIRE_NEWER,          /* host version must be newer than embedded version */
+        USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, /* similar, but may also be equal */
+        _USER_RECONCILE_MODE_MAX,
+        _USER_RECONCILE_MODE_INVALID = -1,
+} UserReconcileMode;
+
+enum { /* return values */
+        USER_RECONCILE_HOST_WON,
+        USER_RECONCILE_EMBEDDED_WON,
+        USER_RECONCILE_IDENTICAL,
+};
+
+int user_record_reconcile(UserRecord *host, UserRecord *embedded, UserReconcileMode mode, UserRecord **ret);
+int user_record_add_binding(UserRecord *h, UserStorage storage, const char *image_path, sd_id128_t partition_uuid, sd_id128_t luks_uuid, sd_id128_t fs_uuid, const char *luks_cipher, const char *luks_cipher_mode, uint64_t luks_volume_key_size, const char *file_system_type, const char *home_directory, uid_t uid, gid_t gid);
+
+/* Results of the two test functions below. */
+enum {
+        USER_TEST_UNDEFINED, /* Returned by user_record_test_image_path() if the storage type knows no image paths */
+        USER_TEST_ABSENT,
+        USER_TEST_EXISTS,
+        USER_TEST_MOUNTED,   /* Only applies to user_record_test_home_directory(), when the home directory exists. */
+        USER_TEST_MAYBE,     /* Only applies to LUKS devices: block device exists, but we don't know if it's the right one */
+};
+
+int user_record_test_home_directory(UserRecord *h);
+int user_record_test_home_directory_and_warn(UserRecord *h);
+int user_record_test_image_path(UserRecord *h);
+int user_record_test_image_path_and_warn(UserRecord *h);
+
+int user_record_test_secret(UserRecord *h, UserRecord *secret);
+
+int user_record_update_last_changed(UserRecord *h, bool with_password);
+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_pkcs11_protected_authentication_path_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);
+int user_record_bad_authentication(UserRecord *h);
+int user_record_ratelimit(UserRecord *h);
+
+int user_record_is_supported(UserRecord *hr, sd_bus_error *error);
index deecd9b8db30d310a7d2607ec9cbff57c5b72f68..21f647149514ab1a77f3c6ccf6ec15b8169057e0 100644 (file)
@@ -8,7 +8,7 @@
 
 #include "alloc-util.h"
 #include "bus-common-errors.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
 #include "def.h"
 #include "env-file-label.h"
 #include "env-file.h"
index cd4d5414503bbbbb0b220972a96f4561da528ccf..19435f80fea40726960b69848872e6dff57741fa 100644 (file)
@@ -4,9 +4,12 @@
 #include <stdio.h>
 
 #include "alloc-util.h"
+#include "gpt.h"
 #include "id128-print.h"
 #include "main-func.h"
 #include "pretty-print.h"
+#include "strv.h"
+#include "format-table.h"
 #include "terminal-util.h"
 #include "util.h"
 #include "verbs.h"
@@ -63,6 +66,85 @@ static int verb_invocation_id(int argc, char **argv, void *userdata) {
         return id128_pretty_print(id, arg_mode);
 }
 
+static int show_one(Table **table, const char *name, sd_id128_t uuid, bool first) {
+        int r;
+
+        if (arg_mode == ID128_PRINT_PRETTY) {
+                _cleanup_free_ char *id = NULL;
+
+                id = strreplace(name, "-", "_");
+                if (!id)
+                        return log_oom();
+
+                ascii_strupper(id);
+
+                r = id128_pretty_print_sample(id, uuid);
+                if (r < 0)
+                        return r;
+                if (!first)
+                        puts("");
+                return 0;
+
+        } else {
+                if (!*table) {
+                        *table = table_new("name", "id");
+                        if (!*table)
+                                return log_oom();
+                        table_set_width(*table, 0);
+                }
+
+                return table_add_many(*table,
+                                      TABLE_STRING, name,
+                                      arg_mode == ID128_PRINT_ID128 ? TABLE_ID128 : TABLE_UUID,
+                                      uuid);
+        }
+}
+
+static int verb_show(int argc, char **argv, void *userdata) {
+        _cleanup_(table_unrefp) Table *table = NULL;
+        char **p;
+        int r;
+
+        argv = strv_skip(argv, 1);
+        if (strv_isempty(argv))
+                for (const GptPartitionType *e = gpt_partition_type_table; e->name; e++) {
+                        r = show_one(&table, e->name, e->uuid, e == gpt_partition_type_table);
+                        if (r < 0)
+                                return r;
+                }
+        else
+                STRV_FOREACH(p, argv) {
+                        sd_id128_t uuid;
+                        bool have_uuid;
+                        const char *id;
+
+                        /* Check if the argument is an actual UUID first */
+                        have_uuid = sd_id128_from_string(*p, &uuid) >= 0;
+
+                        if (have_uuid)
+                                id = gpt_partition_type_uuid_to_string(uuid) ?: "XYZ";
+                        else {
+                                r = gpt_partition_type_uuid_from_string(*p, &uuid);
+                                if (r < 0)
+                                        return log_error_errno(r, "Unknown identifier \"%s\".", *p);
+
+                                id = *p;
+                        }
+
+                        r = show_one(&table, id, uuid, p == argv);
+                        if (r < 0)
+                                return r;
+                }
+
+        if (table) {
+                r = table_print(table, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to print table: %m");
+        }
+
+        return 0;
+}
+
 static int help(void) {
         _cleanup_free_ char *link = NULL;
         int r;
@@ -74,10 +156,11 @@ static int help(void) {
         printf("%s [OPTIONS...] COMMAND\n\n"
                "%sGenerate and print 128bit identifiers.%s\n"
                "\nCommands:\n"
-               "  new                     Generate a new id128 string\n"
+               "  new                     Generate a new ID\n"
                "  machine-id              Print the ID of current machine\n"
                "  boot-id                 Print the ID of current boot\n"
                "  invocation-id           Print the ID of current invocation\n"
+               "  show [NAME]             Print one or more well-known IDs\n"
                "  help                    Show this help\n"
                "\nOptions:\n"
                "  -h --help               Show this help\n"
@@ -155,6 +238,7 @@ static int id128_main(int argc, char *argv[]) {
                 { "machine-id",     VERB_ANY, 1,        0,  verb_machine_id    },
                 { "boot-id",        VERB_ANY, 1,        0,  verb_boot_id       },
                 { "invocation-id",  VERB_ANY, 1,        0,  verb_invocation_id },
+                { "show",           VERB_ANY, VERB_ANY, 0,  verb_show          },
                 { "help",           VERB_ANY, VERB_ANY, 0,  verb_help          },
                 {}
         };
index 96cf696652f684ac866a06aed4c41046365f1062..5f21033db5a04ab01aef226175528cef7219819b 100644 (file)
@@ -247,6 +247,15 @@ int curl_glue_make(CURL **ret, const char *url, void *userdata) {
         if (curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK)
                 return -EIO;
 
+        if (curl_easy_setopt(c, CURLOPT_NOSIGNAL, 1L) != CURLE_OK)
+                return -EIO;
+
+        if (curl_easy_setopt(c, CURLOPT_LOW_SPEED_TIME, 60L) != CURLE_OK)
+                return -EIO;
+
+        if (curl_easy_setopt(c, CURLOPT_LOW_SPEED_LIMIT, 30L) != CURLE_OK)
+                return -EIO;
+
         *ret = TAKE_PTR(c);
         return 0;
 }
index a12a6d63b139a74a516f8f7b4aebeabdeea0c227..0606cf5406b38defd706c7759b135881301b0fa0 100644 (file)
@@ -7,7 +7,6 @@
 
 #include "alloc-util.h"
 #include "btrfs-util.h"
-#include "chattr-util.h"
 #include "copy.h"
 #include "fd-util.h"
 #include "fs-util.h"
@@ -174,9 +173,7 @@ static int raw_import_maybe_convert_qcow2(RawImport *i) {
         if (converted_fd < 0)
                 return log_error_errno(errno, "Failed to create %s: %m", t);
 
-        r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
-        if (r < 0)
-                log_warning_errno(r, "Failed to set file attributes on %s: %m", t);
+        (void) import_set_nocow_and_log(converted_fd, t);
 
         log_info("Unpacking QCOW2 file.");
 
@@ -259,10 +256,7 @@ static int raw_import_open_disk(RawImport *i) {
         if (i->output_fd < 0)
                 return log_error_errno(errno, "Failed to open destination %s: %m", i->temp_path);
 
-        r = chattr_fd(i->output_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
-        if (r < 0)
-                log_warning_errno(r, "Failed to set file attributes on %s: %m", i->temp_path);
-
+        (void) import_set_nocow_and_log(i->output_fd, i->temp_path);
         return 0;
 }
 
index a75ab6bc070b9dcb2f450f054d5f210ca7819b9b..93e704ed612de17304155811fa77da99aaf8472d 100644 (file)
@@ -7,7 +7,7 @@
 
 #include "alloc-util.h"
 #include "bus-common-errors.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
 #include "def.h"
 #include "fd-util.h"
 #include "float.h"
index 4f76421bc7954557446c33dca1f125d141252d4c..51c12444e00af53491ff855f1b55aa24447795ea 100644 (file)
@@ -8,7 +8,6 @@
 
 #include "alloc-util.h"
 #include "btrfs-util.h"
-#include "chattr-util.h"
 #include "copy.h"
 #include "curl-util.h"
 #include "fd-util.h"
@@ -242,9 +241,7 @@ static int raw_pull_maybe_convert_qcow2(RawPull *i) {
         if (converted_fd < 0)
                 return log_error_errno(errno, "Failed to create %s: %m", t);
 
-        r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
-        if (r < 0)
-                log_warning_errno(r, "Failed to set file attributes on %s: %m", t);
+        (void) import_set_nocow_and_log(converted_fd, t);
 
         log_info("Unpacking QCOW2 file.");
 
@@ -354,13 +351,9 @@ static int raw_pull_make_local_copy(RawPull *i) {
         if (dfd < 0)
                 return log_error_errno(errno, "Failed to create writable copy of image: %m");
 
-        /* Turn off COW writing. This should greatly improve
-         * performance on COW file systems like btrfs, since it
-         * reduces fragmentation caused by not allowing in-place
-         * writes. */
-        r = chattr_fd(dfd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
-        if (r < 0)
-                log_warning_errno(r, "Failed to set file attributes on %s: %m", tp);
+        /* Turn off COW writing. This should greatly improve performance on COW file systems like btrfs,
+         * since it reduces fragmentation caused by not allowing in-place writes. */
+        (void) import_set_nocow_and_log(dfd, tp);
 
         r = copy_bytes(i->raw_job->disk_fd, dfd, (uint64_t) -1, COPY_REFLINK);
         if (r < 0) {
@@ -600,10 +593,7 @@ static int raw_pull_job_on_open_disk_raw(PullJob *j) {
         if (r < 0)
                 return r;
 
-        r = chattr_fd(j->disk_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
-        if (r < 0)
-                log_warning_errno(r, "Failed to set file attributes on %s, ignoring: %m", i->temp_path);
-
+        (void) import_set_nocow_and_log(j->disk_fd, i->temp_path);
         return 0;
 }
 
index cc968252887104cf185a969d7b0b5da1325c4815..031e82587d9005a58435f8c88f3053b035bc5f62 100644 (file)
@@ -754,9 +754,13 @@ static int open_journal(sd_journal **j) {
                 r = sd_journal_open_directory(j, arg_directory, arg_journal_type);
         else if (arg_file)
                 r = sd_journal_open_files(j, (const char**) arg_file, 0);
-        else if (arg_machine)
+        else if (arg_machine) {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+                /* FIXME: replace with D-Bus call OpenMachineRootDirectory() so that things also work with raw disk images */
                 r = sd_journal_open_container(j, arg_machine, 0);
-        else
+#pragma GCC diagnostic pop
+        } else
                 r = sd_journal_open(j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type);
         if (r < 0)
                 log_error_errno(r, "Failed to open %s: %m",
index 505191999bd88389335a2cd23537224e12641204..bd5363586077bda3a7d3b724a70ccbe098807935 100644 (file)
@@ -601,7 +601,7 @@ static int journal_file_verify_header(JournalFile *f) {
         return 0;
 }
 
-static int journal_file_fstat(JournalFile *f) {
+int journal_file_fstat(JournalFile *f) {
         int r;
 
         assert(f);
index 502f1f567d1e653aebce8bab10c91f61b88941d5..cf0f7691fb187f933d8b39467ea56dafd88b2fc5 100644 (file)
@@ -145,6 +145,7 @@ int journal_file_open(
 int journal_file_set_offline(JournalFile *f, bool wait);
 bool journal_file_is_offlining(JournalFile *f);
 JournalFile* journal_file_close(JournalFile *j);
+int journal_file_fstat(JournalFile *f);
 DEFINE_TRIVIAL_CLEANUP_FUNC(JournalFile*, journal_file_close);
 
 int journal_file_open_reliably(
index 80a69da4d8cc2691f04d59096526b5cbf2145698..1454df602b8594ad8466d8f2121c59c74328d3b9 100644 (file)
@@ -69,6 +69,7 @@ struct sd_journal {
 
         char *path;
         char *prefix;
+        char *namespace;
 
         OrderedHashmap *files;
         IteratedCache *files_cache;
index 17565abe2128b53d9a9e8578289fc88ea54c8c9c..e5feec83bce64ff1cefbdd6d52a4aabda5595fa0 100644 (file)
@@ -62,6 +62,7 @@
 #include "sigbus.h"
 #include "string-table.h"
 #include "strv.h"
+#include "stdio-util.h"
 #include "syslog-util.h"
 #include "terminal-util.h"
 #include "tmpfile-util.h"
 #include "varlink.h"
 
 #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
-
 #define PROCESS_INOTIFY_INTERVAL 1024   /* Every 1,024 messages processed */
 
-#if HAVE_PCRE2
-DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_match_data*, pcre2_match_data_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_code*, pcre2_code_free);
-
-static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out) {
-        int errorcode, r;
-        PCRE2_SIZE erroroffset;
-        pcre2_code *p;
-
-        p = pcre2_compile((PCRE2_SPTR8) pattern,
-                          PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL);
-        if (!p) {
-                unsigned char buf[LINE_MAX];
-
-                r = pcre2_get_error_message(errorcode, buf, sizeof buf);
-
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Bad pattern \"%s\": %s", pattern,
-                                       r < 0 ? "unknown error" : (char *)buf);
-        }
-
-        *out = p;
-        return 0;
-}
-
-#endif
-
 enum {
         /* Special values for arg_lines */
         ARG_LINES_DEFAULT = -2,
@@ -129,6 +102,7 @@ static const char *arg_directory = NULL;
 static char **arg_file = NULL;
 static bool arg_file_stdin = false;
 static int arg_priorities = 0xFF;
+static Set *arg_facilities = NULL;
 static char *arg_verify_key = NULL;
 #if HAVE_GCRYPT
 static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
@@ -143,13 +117,14 @@ static const char *arg_field = NULL;
 static bool arg_catalog = false;
 static bool arg_reverse = false;
 static int arg_journal_type = 0;
+static int arg_namespace_flags = 0;
 static char *arg_root = NULL;
 static const char *arg_machine = NULL;
+static const char *arg_namespace = NULL;
 static uint64_t arg_vacuum_size = 0;
 static uint64_t arg_vacuum_n_files = 0;
 static usec_t arg_vacuum_time = 0;
 static char **arg_output_fields = NULL;
-
 #if HAVE_PCRE2
 static const char *arg_pattern = NULL;
 static pcre2_code *arg_compiled_pattern = NULL;
@@ -184,6 +159,33 @@ typedef struct BootId {
         LIST_FIELDS(struct BootId, boot_list);
 } BootId;
 
+#if HAVE_PCRE2
+DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_match_data*, pcre2_match_data_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_code*, pcre2_code_free);
+
+static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out) {
+        int errorcode, r;
+        PCRE2_SIZE erroroffset;
+        pcre2_code *p;
+
+        p = pcre2_compile((PCRE2_SPTR8) pattern,
+                          PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL);
+        if (!p) {
+                unsigned char buf[LINE_MAX];
+
+                r = pcre2_get_error_message(errorcode, buf, sizeof buf);
+
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Bad pattern \"%s\": %s", pattern,
+                                       r < 0 ? "unknown error" : (char *)buf);
+        }
+
+        *out = p;
+        return 0;
+}
+
+#endif
+
 static int add_matches_for_device(sd_journal *j, const char *devpath) {
         _cleanup_(sd_device_unrefp) sd_device *device = NULL;
         sd_device *d = NULL;
@@ -303,6 +305,21 @@ static int parse_boot_descriptor(const char *x, sd_id128_t *boot_id, int *offset
         return 1;
 }
 
+static int help_facilities(void) {
+        if (!arg_quiet)
+                puts("Available facilities:");
+
+        for (int i = 0; i < LOG_NFACILITIES; i++) {
+                _cleanup_free_ char *t = NULL;
+
+                if (log_facility_unshifted_to_string_alloc(i, &t))
+                        return log_oom();
+                puts(t);
+        }
+
+        return 0;
+}
+
 static int help(void) {
         _cleanup_free_ char *link = NULL;
         int r;
@@ -313,9 +330,9 @@ static int help(void) {
         if (r < 0)
                 return log_oom();
 
-        printf("%s [OPTIONS...] [MATCHES...]\n\n"
-               "%sQuery the journal.%s\n\n"
-               "Options:\n"
+        printf("%1$s [OPTIONS...] [MATCHES...]\n\n"
+               "%5$sQuery the journal.%6$s\n\n"
+               "%3$sOptions:%4$s\n"
                "     --system                Show the system journal\n"
                "     --user                  Show the user journal for the current user\n"
                "  -M --machine=CONTAINER     Operate on local container\n"
@@ -332,6 +349,7 @@ static int help(void) {
                "     --user-unit=UNIT        Show logs from the specified user unit\n"
                "  -t --identifier=STRING     Show entries with the specified syslog identifier\n"
                "  -p --priority=RANGE        Show entries with the specified priority\n"
+               "     --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"
                "  -e --pager-end             Immediately jump to the end in the pager\n"
@@ -356,10 +374,11 @@ static int help(void) {
                "  -D --directory=PATH        Show journal files from directory\n"
                "     --file=PATH             Show journal file\n"
                "     --root=ROOT             Operate on files below a root directory\n"
+               "     --namespace=NAMESPACE   Show journal data from specified namespace\n"
                "     --interval=TIME         Time interval for changing the FSS sealing key\n"
                "     --verify-key=KEY        Specify FSS verification key\n"
                "     --force                 Override of the FSS key pair with --setup-keys\n"
-               "\nCommands:\n"
+               "\n%3$sCommands:%4$s\n"
                "  -h --help                  Show this help text\n"
                "     --version               Show package version\n"
                "  -N --fields                List all field names currently used\n"
@@ -379,10 +398,11 @@ static int help(void) {
                "     --dump-catalog          Show entries in the message catalog\n"
                "     --update-catalog        Update the message catalog database\n"
                "     --setup-keys            Generate a new FSS key pair\n"
-               "\nSee the %s for details.\n"
+               "\nSee the %2$s for details.\n"
                , program_invocation_short_name
-               , ansi_highlight(), ansi_normal()
                , link
+               , ansi_underline(), ansi_normal()
+               , ansi_highlight(), ansi_normal()
         );
 
         return 0;
@@ -402,6 +422,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_SYSTEM,
                 ARG_ROOT,
                 ARG_HEADER,
+                ARG_FACILITY,
                 ARG_SETUP_KEYS,
                 ARG_FILE,
                 ARG_INTERVAL,
@@ -428,6 +449,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_VACUUM_TIME,
                 ARG_NO_HOSTNAME,
                 ARG_OUTPUT_FIELDS,
+                ARG_NAMESPACE,
         };
 
         static const struct option options[] = {
@@ -458,6 +480,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "header",               no_argument,       NULL, ARG_HEADER               },
                 { "identifier",           required_argument, NULL, 't'                      },
                 { "priority",             required_argument, NULL, 'p'                      },
+                { "facility",             required_argument, NULL, ARG_FACILITY             },
                 { "grep",                 required_argument, NULL, 'g'                      },
                 { "case-sensitive",       optional_argument, NULL, ARG_CASE_SENSITIVE       },
                 { "setup-keys",           no_argument,       NULL, ARG_SETUP_KEYS           },
@@ -492,6 +515,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "vacuum-time",          required_argument, NULL, ARG_VACUUM_TIME          },
                 { "no-hostname",          no_argument,       NULL, ARG_NO_HOSTNAME          },
                 { "output-fields",        required_argument, NULL, ARG_OUTPUT_FIELDS        },
+                { "namespace",            required_argument, NULL, ARG_NAMESPACE            },
                 {}
         };
 
@@ -533,10 +557,8 @@ static int parse_argv(int argc, char *argv[]) {
                         }
 
                         arg_output = output_mode_from_string(optarg);
-                        if (arg_output < 0) {
-                                log_error("Unknown output format '%s'.", optarg);
-                                return -EINVAL;
-                        }
+                        if (arg_output < 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown output format '%s'.", optarg);
 
                         if (IN_SET(arg_output, OUTPUT_EXPORT, OUTPUT_JSON, OUTPUT_JSON_PRETTY, OUTPUT_JSON_SSE, OUTPUT_JSON_SEQ, OUTPUT_CAT))
                                 arg_quiet = true;
@@ -561,10 +583,8 @@ static int parse_argv(int argc, char *argv[]) {
                                         arg_lines = ARG_LINES_ALL;
                                 else {
                                         r = safe_atoi(optarg, &arg_lines);
-                                        if (r < 0 || arg_lines < 0) {
-                                                log_error("Failed to parse lines '%s'", optarg);
-                                                return -EINVAL;
-                                        }
+                                        if (r < 0 || arg_lines < 0)
+                                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse lines '%s'", optarg);
                                 }
                         } else {
                                 arg_lines = 10;
@@ -656,6 +676,23 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_machine = optarg;
                         break;
 
+                case ARG_NAMESPACE:
+                        if (streq(optarg, "*")) {
+                                arg_namespace_flags = SD_JOURNAL_ALL_NAMESPACES;
+                                arg_namespace = NULL;
+                        } else if (startswith(optarg, "+")) {
+                                arg_namespace_flags = SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE;
+                                arg_namespace = optarg + 1;
+                        } else if (isempty(optarg)) {
+                                arg_namespace_flags = 0;
+                                arg_namespace = NULL;
+                        } else {
+                                arg_namespace_flags = 0;
+                                arg_namespace = optarg;
+                        }
+
+                        break;
+
                 case 'D':
                         arg_directory = optarg;
                         break;
@@ -710,30 +747,24 @@ static int parse_argv(int argc, char *argv[]) {
 
                 case ARG_VACUUM_SIZE:
                         r = parse_size(optarg, 1024, &arg_vacuum_size);
-                        if (r < 0) {
-                                log_error("Failed to parse vacuum size: %s", optarg);
-                                return r;
-                        }
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse vacuum size: %s", optarg);
 
                         arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
                         break;
 
                 case ARG_VACUUM_FILES:
                         r = safe_atou64(optarg, &arg_vacuum_n_files);
-                        if (r < 0) {
-                                log_error("Failed to parse vacuum files: %s", optarg);
-                                return r;
-                        }
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse vacuum files: %s", optarg);
 
                         arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
                         break;
 
                 case ARG_VACUUM_TIME:
                         r = parse_sec(optarg, &arg_vacuum_time);
-                        if (r < 0) {
-                                log_error("Failed to parse vacuum time: %s", optarg);
-                                return r;
-                        }
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse vacuum time: %s", optarg);
 
                         arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
                         break;
@@ -748,7 +779,6 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_VERIFY_KEY:
-                        arg_action = ACTION_VERIFY;
                         r = free_and_strdup(&arg_verify_key, optarg);
                         if (r < 0)
                                 return r;
@@ -756,23 +786,23 @@ static int parse_argv(int argc, char *argv[]) {
                          * in ps or htop output. */
                         memset(optarg, 'x', strlen(optarg));
 
+                        arg_action = ACTION_VERIFY;
                         arg_merge = false;
                         break;
 
                 case ARG_INTERVAL:
                         r = parse_sec(optarg, &arg_interval);
-                        if (r < 0 || arg_interval <= 0) {
-                                log_error("Failed to parse sealing key change interval: %s", optarg);
-                                return -EINVAL;
-                        }
+                        if (r < 0 || arg_interval <= 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "Failed to parse sealing key change interval: %s", optarg);
                         break;
 #else
                 case ARG_SETUP_KEYS:
                 case ARG_VERIFY_KEY:
                 case ARG_INTERVAL:
                 case ARG_FORCE:
-                        log_error("Compiled without forward-secure sealing support.");
-                        return -EOPNOTSUPP;
+                        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                               "Compiled without forward-secure sealing support.");
 #endif
 
                 case 'p': {
@@ -780,7 +810,7 @@ static int parse_argv(int argc, char *argv[]) {
 
                         dots = strstr(optarg, "..");
                         if (dots) {
-                                char *a;
+                                _cleanup_free_ char *a = NULL;
                                 int from, to, i;
 
                                 /* a range */
@@ -790,12 +820,10 @@ static int parse_argv(int argc, char *argv[]) {
 
                                 from = log_level_from_string(a);
                                 to = log_level_from_string(dots + 2);
-                                free(a);
 
-                                if (from < 0 || to < 0) {
-                                        log_error("Failed to parse log level range %s", optarg);
-                                        return -EINVAL;
-                                }
+                                if (from < 0 || to < 0)
+                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                               "Failed to parse log level range %s", optarg);
 
                                 arg_priorities = 0;
 
@@ -811,10 +839,9 @@ static int parse_argv(int argc, char *argv[]) {
                                 int p, i;
 
                                 p = log_level_from_string(optarg);
-                                if (p < 0) {
-                                        log_error("Unknown log level %s", optarg);
-                                        return -EINVAL;
-                                }
+                                if (p < 0)
+                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                               "Unknown log level %s", optarg);
 
                                 arg_priorities = 0;
 
@@ -825,6 +852,41 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
+                case ARG_FACILITY: {
+                        const char *p;
+
+                        for (p = optarg;;) {
+                                _cleanup_free_ char *fac = NULL;
+                                int num;
+
+                                r = extract_first_word(&p, &fac, ",", 0);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse facilities: %s", optarg);
+                                if (r == 0)
+                                        break;
+
+                                if (streq(fac, "help")) {
+                                        help_facilities();
+                                        return 0;
+                                }
+
+                                num = log_facility_unshifted_from_string(fac);
+                                if (num < 0)
+                                        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)
+                                        return log_oom();
+                        }
+
+                        break;
+                }
+
 #if HAVE_PCRE2
                 case 'g':
                         arg_pattern = optarg;
@@ -848,19 +910,17 @@ static int parse_argv(int argc, char *argv[]) {
 
                 case 'S':
                         r = parse_timestamp(optarg, &arg_since);
-                        if (r < 0) {
-                                log_error("Failed to parse timestamp: %s", optarg);
-                                return -EINVAL;
-                        }
+                        if (r < 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "Failed to parse timestamp: %s", optarg);
                         arg_since_set = true;
                         break;
 
                 case 'U':
                         r = parse_timestamp(optarg, &arg_until);
-                        if (r < 0) {
-                                log_error("Failed to parse timestamp: %s", optarg);
-                                return -EINVAL;
-                        }
+                        if (r < 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "Failed to parse timestamp: %s", optarg);
                         arg_until_set = true;
                         break;
 
@@ -1417,7 +1477,6 @@ static int add_boot(sd_journal *j) {
          * so take the slow path if log location is specified. */
         if (arg_boot_offset == 0 && sd_id128_is_null(arg_boot_id) &&
             !arg_directory && !arg_file && !arg_root)
-
                 return add_match_this_boot(j, arg_machine);
 
         boot_id = arg_boot_id;
@@ -1672,6 +1731,24 @@ static int add_priorities(sd_journal *j) {
         return 0;
 }
 
+static int add_facilities(sd_journal *j) {
+        void *p;
+        Iterator it;
+        int r;
+
+        SET_FOREACH(p, arg_facilities, it) {
+                char match[STRLEN("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)];
+
+                xsprintf(match, "SYSLOG_FACILITY=%d", PTR_TO_INT(p));
+
+                r = sd_journal_add_match(j, match, strlen(match));
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add match: %m");
+        }
+
+        return 0;
+}
+
 static int add_syslog_identifier(sd_journal *j) {
         int r;
         char **i;
@@ -1944,15 +2021,19 @@ static int verify(sd_journal *j) {
 
 static int simple_varlink_call(const char *option, const char *method) {
         _cleanup_(varlink_flush_close_unrefp) Varlink *link = NULL;
-        const char *error;
+        const char *error, *fn;
         int r;
 
         if (arg_machine)
                 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "%s is not supported in conjunction with --machine=.", option);
 
-        r = varlink_connect_address(&link, "/run/systemd/journal/io.systemd.journal");
+        fn = arg_namespace ?
+                strjoina("/run/systemd/journal.", arg_namespace, "/io.systemd.journal") :
+                "/run/systemd/journal/io.systemd.journal";
+
+        r = varlink_connect_address(&link, fn);
         if (r < 0)
-                return log_error_errno(r, "Failed to connect to /run/systemd/journal/io.systemd.journal: %m");
+                return log_error_errno(r, "Failed to connect to %s: %m", fn);
 
         (void) varlink_set_description(link, "journal");
         (void) varlink_set_relative_timeout(link, USEC_INFINITY);
@@ -2122,10 +2203,9 @@ int main(int argc, char *argv[]) {
                 r = sd_journal_open_directory(&j, arg_directory, arg_journal_type);
         else if (arg_root)
                 r = sd_journal_open_directory(&j, arg_root, arg_journal_type | SD_JOURNAL_OS_ROOT);
-        else if (arg_file_stdin) {
-                int ifd = STDIN_FILENO;
-                r = sd_journal_open_files_fd(&j, &ifd, 1, 0);
-        } else if (arg_file)
+        else if (arg_file_stdin)
+                r = sd_journal_open_files_fd(&j, (int[]) { STDIN_FILENO }, 1, 0);
+        else if (arg_file)
                 r = sd_journal_open_files(&j, (const char**) arg_file, 0);
         else if (arg_machine) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -2136,8 +2216,7 @@ int main(int argc, char *argv[]) {
                 if (geteuid() != 0) {
                         /* The file descriptor returned by OpenMachineRootDirectory() will be owned by users/groups of
                          * the container, thus we need root privileges to override them. */
-                        log_error("Using the --machine= switch requires root privileges.");
-                        r = -EPERM;
+                        r = log_error_errno(SYNTHETIC_ERRNO(EPERM), "Using the --machine= switch requires root privileges.");
                         goto finish;
                 }
 
@@ -2177,7 +2256,11 @@ int main(int argc, char *argv[]) {
                 if (r < 0)
                         safe_close(fd);
         } else
-                r = sd_journal_open(&j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type);
+                r = sd_journal_open_namespace(
+                                &j,
+                                arg_namespace,
+                                (arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY) |
+                                arg_namespace_flags | arg_journal_type);
         if (r < 0) {
                 log_error_errno(r, "Failed to open %s: %m", arg_directory ?: arg_file ? "files" : "journal");
                 goto finish;
@@ -2304,6 +2387,10 @@ int main(int argc, char *argv[]) {
         if (r < 0)
                 goto finish;
 
+        r = add_facilities(j);
+        if (r < 0)
+                goto finish;
+
         r = add_matches(j, argv + optind);
         if (r < 0)
                 goto finish;
@@ -2671,6 +2758,7 @@ finish:
 
         strv_free(arg_file);
 
+        set_free(arg_facilities);
         strv_free(arg_syslog_identifier);
         strv_free(arg_system_units);
         strv_free(arg_user_units);
index cd4cfbee48c78b1216a7f7f8bf86a2f06b52f359..bac67036b0c94b7177f29e89f41df9dbc8d3c210 100644 (file)
@@ -117,23 +117,24 @@ static int client_context_new(Server *s, pid_t pid, ClientContext **ret) {
         if (r < 0)
                 return r;
 
-        c = new0(ClientContext, 1);
+        c = new(ClientContext, 1);
         if (!c)
                 return -ENOMEM;
 
-        c->pid = pid;
-
-        c->uid = UID_INVALID;
-        c->gid = GID_INVALID;
-        c->auditid = AUDIT_SESSION_INVALID;
-        c->loginuid = UID_INVALID;
-        c->owner_uid = UID_INVALID;
-        c->lru_index = PRIOQ_IDX_NULL;
-        c->timestamp = USEC_INFINITY;
-        c->extra_fields_mtime = NSEC_INFINITY;
-        c->log_level_max = -1;
-        c->log_ratelimit_interval = s->ratelimit_interval;
-        c->log_ratelimit_burst = s->ratelimit_burst;
+        *c = (ClientContext) {
+                .pid = pid,
+                .uid = UID_INVALID,
+                .gid = GID_INVALID,
+                .auditid = AUDIT_SESSION_INVALID,
+                .loginuid = UID_INVALID,
+                .owner_uid = UID_INVALID,
+                .lru_index = PRIOQ_IDX_NULL,
+                .timestamp = USEC_INFINITY,
+                .extra_fields_mtime = NSEC_INFINITY,
+                .log_level_max = -1,
+                .log_ratelimit_interval = s->ratelimit_interval,
+                .log_ratelimit_burst = s->ratelimit_burst,
+        };
 
         r = hashmap_put(s->client_contexts, PID_TO_PTR(pid), c);
         if (r < 0) {
@@ -779,7 +780,9 @@ void client_context_acquire_default(Server *s) {
                         log_warning_errno(r, "Failed to acquire our own context, ignoring: %m");
         }
 
-        if (!s->pid1_context) {
+        if (!s->namespace && !s->pid1_context) {
+                /* Acquire PID1's context, but only if we are in non-namespaced mode, since PID 1 is only
+                 * going to log to the non-namespaced journal instance. */
 
                 r = client_context_acquire(s, 1, NULL, NULL, 0, NULL, &s->pid1_context);
                 if (r < 0)
index 366298758c65f006bb7fce0d9e6911f8c01fe920..ec404145eec906c53616fb4bbbf85f99ee9b6a0f 100644 (file)
 #include "string-util.h"
 
 void server_forward_kmsg(
-        Server *s,
-        int priority,
-        const char *identifier,
-        const char *message,
-        const struct ucred *ucred) {
+                Server *s,
+                int priority,
+                const char *identifier,
+                const char *message,
+                const struct ucred *ucred) {
 
         _cleanup_free_ char *ident_buf = NULL;
         struct iovec iovec[5];
@@ -416,19 +416,23 @@ fail:
 }
 
 int server_open_kernel_seqnum(Server *s) {
-        _cleanup_close_ int fd;
+        _cleanup_close_ int fd = -1;
+        const char *fn;
         uint64_t *p;
         int r;
 
         assert(s);
 
-        /* We store the seqnum we last read in an mmaped file. That
-         * way we can just use it like a variable, but it is
-         * persistent and automatically flushed at reboot. */
+        /* We store the seqnum we last read in an mmaped file. That way we can just use it like a variable,
+         * but it is persistent and automatically flushed at reboot. */
 
-        fd = open("/run/systemd/journal/kernel-seqnum", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644);
+        if (!s->read_kmsg)
+                return 0;
+
+        fn = strjoina(s->runtime_directory, "/kernel-seqnum");
+        fd = open(fn, O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644);
         if (fd < 0) {
-                log_error_errno(errno, "Failed to open /run/systemd/journal/kernel-seqnum, ignoring: %m");
+                log_error_errno(errno, "Failed to open %s, ignoring: %m", fn);
                 return 0;
         }
 
index 73a96da9f7a99d65b91f39a559dd1a0fe328997d..f2b867da3d3cd5e1993157fe1217df3f8a2f4db9 100644 (file)
@@ -450,24 +450,28 @@ void server_process_native_file(
         }
 }
 
-int server_open_native_socket(Server *s) {
-
-        static const union sockaddr_union sa = {
-                .un.sun_family = AF_UNIX,
-                .un.sun_path = "/run/systemd/journal/socket",
-        };
+int server_open_native_socket(Server *s, const char *native_socket) {
         int r;
 
         assert(s);
+        assert(native_socket);
 
         if (s->native_fd < 0) {
+                union sockaddr_union sa;
+                size_t sa_len;
+
+                r = sockaddr_un_set_path(&sa.un, native_socket);
+                if (r < 0)
+                        return log_error_errno(r, "Unable to use namespace path %s for AF_UNIX socket: %m", native_socket);
+                sa_len = r;
+
                 s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
                 if (s->native_fd < 0)
                         return log_error_errno(errno, "socket() failed: %m");
 
                 (void) sockaddr_un_unlink(&sa.un);
 
-                r = bind(s->native_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
+                r = bind(s->native_fd, &sa.sa, sa_len);
                 if (r < 0)
                         return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
 
index 2a33ef74c5a39e309f368fd7801cbce160dcf5e2..8d06636262ef933210b9f1e8298fd908565e74be 100644 (file)
@@ -20,4 +20,4 @@ void server_process_native_file(
                 const char *label,
                 size_t label_len);
 
-int server_open_native_socket(Server *s);
+int server_open_native_socket(Server *s, const char *native_socket);
index af7196c6dee35266588f72834d19a3e78d8bd371..fa21e7da29c8c226fef3beea79d9fdac107e63fc 100644 (file)
@@ -99,7 +99,7 @@ void journal_ratelimit_free(JournalRateLimit *r) {
         free(r);
 }
 
-_pure_ static bool journal_ratelimit_group_expired(JournalRateLimitGroup *g, usec_t ts) {
+static bool journal_ratelimit_group_expired(JournalRateLimitGroup *g, usec_t ts) {
         unsigned i;
 
         assert(g);
index f6caf0e212ab02fb7ba714696abb58691998bdca..489730df8ff7e1af884dd92c933eb6d2385aa0ff 100644 (file)
@@ -44,6 +44,7 @@
 #include "missing_audit.h"
 #include "mkdir.h"
 #include "parse-util.h"
+#include "path-util.h"
 #include "proc-cmdline.h"
 #include "process-util.h"
 #include "rm-rf.h"
 
 #define DEFERRED_CLOSES_MAX (4096)
 
-static int determine_path_usage(Server *s, const char *path, uint64_t *ret_used, uint64_t *ret_free) {
+#define IDLE_TIMEOUT_USEC (30*USEC_PER_SEC)
+
+static int determine_path_usage(
+                Server *s,
+                const char *path,
+                uint64_t *ret_used,
+                uint64_t *ret_free) {
+
         _cleanup_closedir_ DIR *d = NULL;
         struct dirent *de;
         struct statvfs ss;
 
+        assert(s);
+        assert(path);
         assert(ret_used);
         assert(ret_free);
 
@@ -163,13 +173,19 @@ static void patch_min_use(JournalStorage *storage) {
         storage->metrics.min_use = MAX(storage->metrics.min_use, storage->space.vfs_used);
 }
 
+static JournalStorage* server_current_storage(Server *s) {
+        assert(s);
+
+        return s->system_journal ? &s->system_storage : &s->runtime_storage;
+}
+
 static int determine_space(Server *s, uint64_t *available, uint64_t *limit) {
         JournalStorage *js;
         int r;
 
         assert(s);
 
-        js = s->system_journal ? &s->system_storage : &s->runtime_storage;
+        js = server_current_storage(s);
 
         r = cache_space_refresh(s, js);
         if (r >= 0) {
@@ -189,7 +205,7 @@ void server_space_usage_message(Server *s, JournalStorage *storage) {
         assert(s);
 
         if (!storage)
-                storage = s->system_journal ? &s->system_storage : &s->runtime_storage;
+                storage = server_current_storage(s);
 
         if (cache_space_refresh(s, storage) < 0)
                 return;
@@ -280,8 +296,18 @@ static int open_journal(
         return r;
 }
 
-static bool flushed_flag_is_set(void) {
-        return access("/run/systemd/journal/flushed", F_OK) >= 0;
+static bool flushed_flag_is_set(Server *s) {
+        const char *fn;
+
+        assert(s);
+
+        /* We don't support the "flushing" concept for namespace instances, we assume them to always have
+         * access to /var */
+        if (s->namespace)
+                return true;
+
+        fn = strjoina(s->runtime_directory, "/flushed");
+        return access(fn, F_OK) >= 0;
 }
 
 static int system_journal_open(Server *s, bool flush_requested, bool relinquish_requested) {
@@ -290,17 +316,15 @@ static int system_journal_open(Server *s, bool flush_requested, bool relinquish_
 
         if (!s->system_journal &&
             IN_SET(s->storage, STORAGE_PERSISTENT, STORAGE_AUTO) &&
-            (flush_requested || flushed_flag_is_set()) &&
+            (flush_requested || flushed_flag_is_set(s)) &&
             !relinquish_requested) {
 
-                /* If in auto mode: first try to create the machine
-                 * path, but not the prefix.
+                /* If in auto mode: first try to create the machine path, but not the prefix.
                  *
-                 * If in persistent mode: create /var/log/journal and
-                 * the machine path */
+                 * If in persistent mode: create /var/log/journal and the machine path */
 
                 if (s->storage == STORAGE_PERSISTENT)
-                        (void) mkdir_p("/var/log/journal/", 0755);
+                        (void) mkdir_parents(s->system_storage.path, 0755);
 
                 (void) mkdir(s->system_storage.path, 0755);
 
@@ -317,12 +341,11 @@ static int system_journal_open(Server *s, bool flush_requested, bool relinquish_
                         r = 0;
                 }
 
-                /* If the runtime journal is open, and we're post-flush, we're
-                 * recovering from a failed system journal rotate (ENOSPC)
-                 * for which the runtime journal was reopened.
+                /* If the runtime journal is open, and we're post-flush, we're recovering from a failed
+                 * system journal rotate (ENOSPC) for which the runtime journal was reopened.
                  *
-                 * Perform an implicit flush to var, leaving the runtime
-                 * journal closed, now that the system journal is back.
+                 * Perform an implicit flush to var, leaving the runtime journal closed, now that the system
+                 * journal is back.
                  */
                 if (!flush_requested)
                         (void) server_flush_to_var(s, true);
@@ -349,12 +372,10 @@ static int system_journal_open(Server *s, bool flush_requested, bool relinquish_
 
                 } else {
 
-                        /* OK, we really need the runtime journal, so create
-                         * it if necessary. */
+                        /* OK, we really need the runtime journal, so create it if necessary. */
 
-                        (void) mkdir("/run/log", 0755);
-                        (void) mkdir("/run/log/journal", 0755);
-                        (void) mkdir_parents(fn, 0750);
+                        (void) mkdir_parents(s->runtime_storage.path, 0755);
+                        (void) mkdir(s->runtime_storage.path, 0750);
 
                         r = open_journal(s, true, fn, O_RDWR|O_CREAT, false, &s->runtime_storage.metrics, &s->runtime_journal);
                         if (r < 0)
@@ -373,27 +394,23 @@ static int system_journal_open(Server *s, bool flush_requested, bool relinquish_
 
 static JournalFile* find_journal(Server *s, uid_t uid) {
         _cleanup_free_ char *p = NULL;
-        int r;
         JournalFile *f;
-        sd_id128_t machine;
+        int r;
 
         assert(s);
 
-        /* A rotate that fails to create the new journal (ENOSPC) leaves the
-         * rotated journal as NULL.  Unless we revisit opening, even after
-         * space is made available we'll continue to return NULL indefinitely.
+        /* A rotate that fails to create the new journal (ENOSPC) leaves the rotated journal as NULL.  Unless
+         * we revisit opening, even after space is made available we'll continue to return NULL indefinitely.
          *
-         * system_journal_open() is a noop if the journals are already open, so
-         * we can just call it here to recover from failed rotates (or anything
-         * else that's left the journals as NULL).
+         * system_journal_open() is a noop if the journals are already open, so we can just call it here to
+         * recover from failed rotates (or anything else that's left the journals as NULL).
          *
          * Fixes https://github.com/systemd/systemd/issues/3968 */
         (void) system_journal_open(s, false, false);
 
-        /* We split up user logs only on /var, not on /run. If the
-         * runtime file is open, we write to it exclusively, in order
-         * to guarantee proper order as soon as we flush /run to
-         * /var and close the runtime file. */
+        /* We split up user logs only on /var, not on /run. If the runtime file is open, we write to it
+         * exclusively, in order to guarantee proper order as soon as we flush /run to /var and close the
+         * runtime file. */
 
         if (s->runtime_journal)
                 return s->runtime_journal;
@@ -405,22 +422,14 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
         if (f)
                 return f;
 
-        r = sd_id128_get_machine(&machine);
-        if (r < 0) {
-                log_debug_errno(r, "Failed to determine machine ID, using system log: %m");
-                return s->system_journal;
-        }
-
-        if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/user-"UID_FMT".journal",
-                     SD_ID128_FORMAT_VAL(machine), uid) < 0) {
+        if (asprintf(&p, "%s/user-" UID_FMT ".journal", s->system_storage.path, uid) < 0) {
                 log_oom();
                 return s->system_journal;
         }
 
+        /* Too many open? Then let's close one (or more) */
         while (ordered_hashmap_size(s->user_journals) >= USER_JOURNALS_MAX) {
-                /* Too many open? Then let's close one */
-                f = ordered_hashmap_steal_first(s->user_journals);
-                assert(f);
+                assert_se(f = ordered_hashmap_steal_first(s->user_journals));
                 (void) journal_file_close(f);
         }
 
@@ -428,14 +437,13 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
         if (r < 0)
                 return s->system_journal;
 
-        server_add_acls(f, uid);
-
         r = ordered_hashmap_put(s->user_journals, UID_TO_PTR(uid), f);
         if (r < 0) {
                 (void) journal_file_close(f);
                 return s->system_journal;
         }
 
+        server_add_acls(f, uid);
         return f;
 }
 
@@ -461,7 +469,6 @@ static int do_rotate(
         }
 
         server_add_acls(*f, uid);
-
         return r;
 }
 
@@ -498,36 +505,110 @@ static void server_vacuum_deferred_closes(Server *s) {
         }
 }
 
-static int open_user_journal_directory(Server *s, DIR **ret_dir, char **ret_path) {
-        _cleanup_closedir_ DIR *dir = NULL;
-        _cleanup_free_ char *path = NULL;
-        sd_id128_t machine;
+static int vacuum_offline_user_journals(Server *s) {
+        _cleanup_closedir_ DIR *d = NULL;
         int r;
 
         assert(s);
 
-        r = sd_id128_get_machine(&machine);
-        if (r < 0)
-                return log_error_errno(r, "Failed to determine machine ID, ignoring: %m");
+        d = opendir(s->system_storage.path);
+        if (!d) {
+                if (errno == ENOENT)
+                        return 0;
 
-        if (asprintf(&path, "/var/log/journal/" SD_ID128_FORMAT_STR "/", SD_ID128_FORMAT_VAL(machine)) < 0)
-                return log_oom();
+                return log_error_errno(errno, "Failed to open %s: %m", s->system_storage.path);
+        }
+
+        for (;;) {
+                _cleanup_free_ char *u = NULL, *full = NULL;
+                _cleanup_close_ int fd = -1;
+                const char *a, *b;
+                struct dirent *de;
+                JournalFile *f;
+                uid_t uid;
+
+                errno = 0;
+                de = readdir_no_dot(d);
+                if (!de) {
+                        if (errno != 0)
+                                log_warning_errno(errno, "Failed to enumerate %s, ignoring: %m", s->system_storage.path);
+
+                        break;
+                }
+
+                a = startswith(de->d_name, "user-");
+                if (!a)
+                        continue;
+                b = endswith(de->d_name, ".journal");
+                if (!b)
+                        continue;
+
+                u = strndup(a, b-a);
+                if (!u)
+                        return log_oom();
+
+                r = parse_uid(u, &uid);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to parse UID from file name '%s', ignoring: %m", de->d_name);
+                        continue;
+                }
+
+                /* Already rotated in the above loop? i.e. is it an open user journal? */
+                if (ordered_hashmap_contains(s->user_journals, UID_TO_PTR(uid)))
+                        continue;
+
+                full = path_join(s->system_storage.path, de->d_name);
+                if (!full)
+                        return log_oom();
+
+                fd = openat(dirfd(d), de->d_name, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
+                if (fd < 0) {
+                        log_full_errno(IN_SET(errno, ELOOP, ENOENT) ? LOG_DEBUG : LOG_WARNING, errno,
+                                       "Failed to open journal file '%s' for rotation: %m", full);
+                        continue;
+                }
+
+                /* Make some room in the set of deferred close()s */
+                server_vacuum_deferred_closes(s);
+
+                /* Open the file briefly, so that we can archive it */
+                r = journal_file_open(fd,
+                                      full,
+                                      O_RDWR,
+                                      0640,
+                                      s->compress.enabled,
+                                      s->compress.threshold_bytes,
+                                      s->seal,
+                                      &s->system_storage.metrics,
+                                      s->mmap,
+                                      s->deferred_closes,
+                                      NULL,
+                                      &f);
+                if (r < 0) {
+                        log_warning_errno(r, "Failed to read journal file %s for rotation, trying to move it out of the way: %m", full);
+
+                        r = journal_file_dispose(dirfd(d), de->d_name);
+                        if (r < 0)
+                                log_warning_errno(r, "Failed to move %s out of the way, ignoring: %m", full);
+                        else
+                                log_debug("Successfully moved %s out of the way.", full);
+
+                        continue;
+                }
+
+                TAKE_FD(fd); /* Donated to journal_file_open() */
 
-        dir = opendir(path);
-        if (!dir)
-                return log_error_errno(errno, "Failed to open user journal directory '%s': %m", path);
+                r = journal_file_archive(f);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to archive journal file '%s', ignoring: %m", full);
 
-        if (ret_dir)
-                *ret_dir = TAKE_PTR(dir);
-        if (ret_path)
-                *ret_path = TAKE_PTR(path);
+                f = journal_initiate_close(f, s->deferred_closes);
+        }
 
         return 0;
 }
 
 void server_rotate(Server *s) {
-        _cleanup_free_ char *path = NULL;
-        _cleanup_closedir_ DIR *d = NULL;
         JournalFile *f;
         Iterator i;
         void *k;
@@ -549,92 +630,10 @@ void server_rotate(Server *s) {
                         ordered_hashmap_remove(s->user_journals, k);
         }
 
-        /* Finally, also rotate all user journals we currently do not have open. (But do so only if we actually have
-         * access to /var, i.e. are not in the log-to-runtime-journal mode). */
-        if (!s->runtime_journal &&
-            open_user_journal_directory(s, &d, &path) >= 0) {
-
-                struct dirent *de;
-
-                FOREACH_DIRENT(de, d, log_warning_errno(errno, "Failed to enumerate %s, ignoring: %m", path)) {
-                        _cleanup_free_ char *u = NULL, *full = NULL;
-                        _cleanup_close_ int fd = -1;
-                        const char *a, *b;
-                        uid_t uid;
-
-                        a = startswith(de->d_name, "user-");
-                        if (!a)
-                                continue;
-                        b = endswith(de->d_name, ".journal");
-                        if (!b)
-                                continue;
-
-                        u = strndup(a, b-a);
-                        if (!u) {
-                                log_oom();
-                                break;
-                        }
-
-                        r = parse_uid(u, &uid);
-                        if (r < 0) {
-                                log_debug_errno(r, "Failed to parse UID from file name '%s', ignoring: %m", de->d_name);
-                                continue;
-                        }
-
-                        /* Already rotated in the above loop? i.e. is it an open user journal? */
-                        if (ordered_hashmap_contains(s->user_journals, UID_TO_PTR(uid)))
-                                continue;
-
-                        full = strjoin(path, de->d_name);
-                        if (!full) {
-                                log_oom();
-                                break;
-                        }
-
-                        fd = openat(dirfd(d), de->d_name, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
-                        if (fd < 0) {
-                                log_full_errno(IN_SET(errno, ELOOP, ENOENT) ? LOG_DEBUG : LOG_WARNING, errno,
-                                               "Failed to open journal file '%s' for rotation: %m", full);
-                                continue;
-                        }
-
-                        /* Make some room in the set of deferred close()s */
-                        server_vacuum_deferred_closes(s);
-
-                        /* Open the file briefly, so that we can archive it */
-                        r = journal_file_open(fd,
-                                              full,
-                                              O_RDWR,
-                                              0640,
-                                              s->compress.enabled,
-                                              s->compress.threshold_bytes,
-                                              s->seal,
-                                              &s->system_storage.metrics,
-                                              s->mmap,
-                                              s->deferred_closes,
-                                              NULL,
-                                              &f);
-                        if (r < 0) {
-                                log_warning_errno(r, "Failed to read journal file %s for rotation, trying to move it out of the way: %m", full);
-
-                                r = journal_file_dispose(dirfd(d), de->d_name);
-                                if (r < 0)
-                                        log_warning_errno(r, "Failed to move %s out of the way, ignoring: %m", full);
-                                else
-                                        log_debug("Successfully moved %s out of the way.", full);
-
-                                continue;
-                        }
-
-                        TAKE_FD(fd); /* Donated to journal_file_open() */
-
-                        r = journal_file_archive(f);
-                        if (r < 0)
-                                log_debug_errno(r, "Failed to archive journal file '%s', ignoring: %m", full);
-
-                        f = journal_initiate_close(f, s->deferred_closes);
-                }
-        }
+        /* Finally, also rotate all user journals we currently do not have open. (But do so only if we
+         * actually have access to /var, i.e. are not in the log-to-runtime-journal mode). */
+        if (!s->runtime_journal)
+                (void) vacuum_offline_user_journals(s);
 
         server_process_deferred_closes(s);
 }
@@ -741,8 +740,7 @@ static void server_cache_hostname(Server *s) {
         if (!x)
                 return;
 
-        free(s->hostname_field);
-        s->hostname_field = x;
+        free_and_replace(s->hostname_field, x);
 }
 
 static bool shall_try_append_again(JournalFile *f, int r) {
@@ -1003,6 +1001,9 @@ static void dispatch_message_real(
         if (!isempty(s->hostname_field))
                 iovec[n++] = IOVEC_MAKE_STRING(s->hostname_field);
 
+        if (!isempty(s->namespace_field))
+                iovec[n++] = IOVEC_MAKE_STRING(s->namespace_field);
+
         assert(n <= m);
 
         if (s->split_mode == SPLIT_UID && c && uid_is_valid(c->uid))
@@ -1115,10 +1116,11 @@ void server_dispatch_message(
 }
 
 int server_flush_to_var(Server *s, bool require_flag_file) {
-        sd_journal *j = NULL;
         char ts[FORMAT_TIMESPAN_MAX];
-        usec_t start;
+        sd_journal *j = NULL;
+        const char *fn;
         unsigned n = 0;
+        usec_t start;
         int r, k;
 
         assert(s);
@@ -1126,10 +1128,13 @@ int server_flush_to_var(Server *s, bool require_flag_file) {
         if (!IN_SET(s->storage, STORAGE_AUTO, STORAGE_PERSISTENT))
                 return 0;
 
-        if (!s->runtime_journal)
+        if (s->namespace) /* Flushing concept does not exist for namespace instances */
                 return 0;
 
-        if (require_flag_file && !flushed_flag_is_set())
+        if (!s->runtime_journal) /* Nothing to flush? */
+                return 0;
+
+        if (require_flag_file && !flushed_flag_is_set(s))
                 return 0;
 
         (void) system_journal_open(s, true, false);
@@ -1137,7 +1142,7 @@ int server_flush_to_var(Server *s, bool require_flag_file) {
         if (!s->system_journal)
                 return 0;
 
-        log_debug("Flushing to /var...");
+        log_debug("Flushing to %s...", s->system_storage.path);
 
         start = now(CLOCK_MONOTONIC);
 
@@ -1197,33 +1202,40 @@ finish:
         s->runtime_journal = journal_file_close(s->runtime_journal);
 
         if (r >= 0)
-                (void) rm_rf("/run/log/journal", REMOVE_ROOT);
+                (void) rm_rf(s->runtime_storage.path, REMOVE_ROOT);
 
         sd_journal_close(j);
 
         server_driver_message(s, 0, NULL,
-                              LOG_MESSAGE("Time spent on flushing to /var is %s for %u entries.",
+                              LOG_MESSAGE("Time spent on flushing to %s is %s for %u entries.",
+                                          s->system_storage.path,
                                           format_timespan(ts, sizeof(ts), now(CLOCK_MONOTONIC) - start, 0),
                                           n),
                               NULL);
 
-        k = touch("/run/systemd/journal/flushed");
+        fn = strjoina(s->runtime_directory, "/flushed");
+        k = touch(fn);
         if (k < 0)
-                log_warning_errno(k, "Failed to touch /run/systemd/journal/flushed, ignoring: %m");
+                log_warning_errno(k, "Failed to touch %s, ignoring: %m", fn);
 
+        server_refresh_idle_timer(s);
         return r;
 }
 
 static int server_relinquish_var(Server *s) {
+        const char *fn;
         assert(s);
 
         if (s->storage == STORAGE_NONE)
                 return 0;
 
+        if (s->namespace) /* Concept does not exist for namespaced instances */
+                return -EOPNOTSUPP;
+
         if (s->runtime_journal && !s->system_journal)
                 return 0;
 
-        log_debug("Relinquishing /var...");
+        log_debug("Relinquishing %s...", s->system_storage.path);
 
         (void) system_journal_open(s, false, true);
 
@@ -1231,13 +1243,20 @@ static int server_relinquish_var(Server *s) {
         ordered_hashmap_clear_with_destructor(s->user_journals, journal_file_close);
         set_clear_with_destructor(s->deferred_closes, journal_file_close);
 
-        if (unlink("/run/systemd/journal/flushed") < 0 && errno != ENOENT)
-                log_warning_errno(errno, "Failed to unlink /run/systemd/journal/flushed, ignoring: %m");
+        fn = strjoina(s->runtime_directory, "/flushed");
+        if (unlink(fn) < 0 && errno != ENOENT)
+                log_warning_errno(errno, "Failed to unlink %s, ignoring: %m", fn);
 
+        server_refresh_idle_timer(s);
         return 0;
 }
 
-int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
+int server_process_datagram(
+                sd_event_source *es,
+                int fd,
+                uint32_t revents,
+                void *userdata) {
+
         Server *s = userdata;
         struct ucred *ucred = NULL;
         struct timeval *tv = NULL;
@@ -1352,6 +1371,8 @@ int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void
         }
 
         close_many(fds, n_fds);
+
+        server_refresh_idle_timer(s);
         return 0;
 }
 
@@ -1363,6 +1384,8 @@ static void server_full_flush(Server *s) {
         server_vacuum(s, false);
 
         server_space_usage_message(s, NULL);
+
+        server_refresh_idle_timer(s);
 }
 
 static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) {
@@ -1370,6 +1393,11 @@ static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo *
 
         assert(s);
 
+        if (s->namespace) {
+                log_error("Received SIGUSR1 signal from PID " PID_FMT ", but flushing runtime journals not supported for namespaced instances.", si->ssi_pid);
+                return 0;
+        }
+
         log_info("Received SIGUSR1 signal from PID " PID_FMT ", as request to flush runtime journal.", si->ssi_pid);
         server_full_flush(s);
 
@@ -1377,6 +1405,7 @@ static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo *
 }
 
 static void server_full_rotate(Server *s) {
+        const char *fn;
         int r;
 
         assert(s);
@@ -1390,9 +1419,10 @@ static void server_full_rotate(Server *s) {
                 patch_min_use(&s->runtime_storage);
 
         /* Let clients know when the most recent rotation happened. */
-        r = write_timestamp_file_atomic("/run/systemd/journal/rotated", now(CLOCK_MONOTONIC));
+        fn = strjoina(s->runtime_directory, "/rotated");
+        r = write_timestamp_file_atomic(fn, now(CLOCK_MONOTONIC));
         if (r < 0)
-                log_warning_errno(r, "Failed to write /run/systemd/journal/rotated, ignoring: %m");
+                log_warning_errno(r, "Failed to write %s, ignoring: %m", fn);
 }
 
 static int dispatch_sigusr2(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) {
@@ -1418,6 +1448,7 @@ static int dispatch_sigterm(sd_event_source *es, const struct signalfd_siginfo *
 }
 
 static void server_full_sync(Server *s) {
+        const char *fn;
         int r;
 
         assert(s);
@@ -1425,9 +1456,10 @@ static void server_full_sync(Server *s) {
         server_sync(s);
 
         /* Let clients know when the most recent sync happened. */
-        r = write_timestamp_file_atomic("/run/systemd/journal/synced", now(CLOCK_MONOTONIC));
+        fn = strjoina(s->runtime_directory, "/synced");
+        r = write_timestamp_file_atomic(fn, now(CLOCK_MONOTONIC));
         if (r < 0)
-                log_warning_errno(r, "Failed to write /run/systemd/journal/synced, ignoring: %m");
+                log_warning_errno(r, "Failed to write %s, ignoring: %m", fn);
 
         return;
 }
@@ -1592,8 +1624,28 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
 }
 
 static int server_parse_config_file(Server *s) {
+        int r;
+
         assert(s);
 
+        if (s->namespace) {
+                const char *namespaced;
+
+                /* 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);
+                if (r < 0)
+                        return r;
+
+                return 0;
+        }
+
         return config_parse_many_nulstr(PKGSYSCONFDIR "/journald.conf",
                                         CONF_PATHS_NULSTR("systemd/journald.conf.d"),
                                         "Journal\0",
@@ -1788,9 +1840,10 @@ static int dispatch_watchdog(sd_event_source *es, uint64_t usec, void *userdata)
 }
 
 static int server_connect_notify(Server *s) {
-        union sockaddr_union sa = {};
+        union sockaddr_union sa;
+        socklen_t sa_len;
         const char *e;
-        int r, salen;
+        int r;
 
         assert(s);
         assert(s->notify_fd < 0);
@@ -1813,9 +1866,10 @@ static int server_connect_notify(Server *s) {
         if (!e)
                 return 0;
 
-        salen = sockaddr_un_set_path(&sa.un, e);
-        if (salen < 0)
-                return log_error_errno(salen, "NOTIFY_SOCKET set to invalid value '%s': %m", e);
+        r = sockaddr_un_set_path(&sa.un, e);
+        if (r < 0)
+                return log_error_errno(r, "NOTIFY_SOCKET set to invalid value '%s': %m", e);
+        sa_len = r;
 
         s->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
         if (s->notify_fd < 0)
@@ -1823,7 +1877,7 @@ static int server_connect_notify(Server *s) {
 
         (void) fd_inc_sndbuf(s->notify_fd, NOTIFY_SNDBUF_SIZE);
 
-        r = connect(s->notify_fd, &sa.sa, salen);
+        r = connect(s->notify_fd, &sa.sa, sa_len);
         if (r < 0)
                 return log_error_errno(errno, "Failed to connect to notify socket: %m");
 
@@ -1936,6 +1990,8 @@ static int vl_method_flush_to_var(Varlink *link, JsonVariant *parameters, Varlin
 
         if (json_variant_elements(parameters) > 0)
                 return varlink_error_invalid_parameter(link, parameters);
+        if (s->namespace)
+                return varlink_error(link, "io.systemd.Journal.NotSupportedByNamespaces", NULL);
 
         log_info("Received client request to flush runtime journal.");
         server_full_flush(s);
@@ -1951,14 +2007,38 @@ static int vl_method_relinquish_var(Varlink *link, JsonVariant *parameters, Varl
 
         if (json_variant_elements(parameters) > 0)
                 return varlink_error_invalid_parameter(link, parameters);
+        if (s->namespace)
+                return varlink_error(link, "io.systemd.Journal.NotSupportedByNamespaces", NULL);
 
-        log_info("Received client request to relinquish /var access.");
+        log_info("Received client request to relinquish %s access.", s->system_storage.path);
         server_relinquish_var(s);
 
         return varlink_reply(link, NULL);
 }
 
-static int server_open_varlink(Server *s) {
+static int vl_connect(VarlinkServer *server, Varlink *link, void *userdata) {
+        Server *s = userdata;
+
+        assert(server);
+        assert(link);
+        assert(s);
+
+        (void) server_start_or_stop_idle_timer(s); /* maybe we are no longer idle */
+
+        return 0;
+}
+
+static void vl_disconnect(VarlinkServer *server, Varlink *link, void *userdata) {
+        Server *s = userdata;
+
+        assert(server);
+        assert(link);
+        assert(s);
+
+        (void) server_start_or_stop_idle_timer(s); /* maybe we are idle now */
+}
+
+static int server_open_varlink(Server *s, const char *socket, int fd) {
         int r;
 
         assert(s);
@@ -1978,7 +2058,18 @@ static int server_open_varlink(Server *s) {
         if (r < 0)
                 return r;
 
-        r = varlink_server_listen_address(s->varlink_server, "/run/systemd/journal/io.systemd.journal", 0600);
+        r = varlink_server_bind_connect(s->varlink_server, vl_connect);
+        if (r < 0)
+                return r;
+
+        r = varlink_server_bind_disconnect(s->varlink_server, vl_disconnect);
+        if (r < 0)
+                return r;
+
+        if (fd < 0)
+                r = varlink_server_listen_address(s->varlink_server, socket, 0600);
+        else
+                r = varlink_server_listen_fd(s->varlink_server, fd);
         if (r < 0)
                 return r;
 
@@ -1989,9 +2080,117 @@ static int server_open_varlink(Server *s) {
         return 0;
 }
 
-int server_init(Server *s) {
+static bool server_is_idle(Server *s) {
+        assert(s);
+
+        /* The server for the main namespace is never idle */
+        if (!s->namespace)
+                return false;
+
+        /* If a retention maximum is set larger than the idle time we need to be running to enforce it, hence
+         * turn off the idle logic. */
+        if (s->max_retention_usec > IDLE_TIMEOUT_USEC)
+                return false;
+
+        /* We aren't idle if we have a varlink client */
+        if (varlink_server_current_connections(s->varlink_server) > 0)
+                return false;
+
+        /* If we have stdout streams we aren't idle */
+        if (s->n_stdout_streams > 0)
+                return false;
+
+        return true;
+}
+
+static int server_idle_handler(sd_event_source *source, uint64_t usec, void *userdata) {
+        Server *s = userdata;
+
+        assert(source);
+        assert(s);
+
+        log_debug("Server is idle, exiting.");
+        sd_event_exit(s->event, 0);
+        return 0;
+}
+
+int server_start_or_stop_idle_timer(Server *s) {
+        _cleanup_(sd_event_source_unrefp) sd_event_source *source = NULL;
+        usec_t when;
+        int r;
+
+        assert(s);
+
+        if (!server_is_idle(s)) {
+                s->idle_event_source = sd_event_source_disable_unref(s->idle_event_source);
+                return 0;
+        }
+
+        if (s->idle_event_source)
+                return 1;
+
+        r = sd_event_now(s->event, CLOCK_MONOTONIC, &when);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine current time: %m");
+
+        r = sd_event_add_time(s->event, &source, CLOCK_MONOTONIC, usec_add(when, IDLE_TIMEOUT_USEC), 0, server_idle_handler, s);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate idle timer: %m");
+
+        r = sd_event_source_set_priority(source, SD_EVENT_PRIORITY_IDLE);
+        if (r < 0)
+                return log_error_errno(r, "Failed to set idle timer priority: %m");
+
+        (void) sd_event_source_set_description(source, "idle-timer");
+
+        s->idle_event_source = TAKE_PTR(source);
+        return 1;
+}
+
+int server_refresh_idle_timer(Server *s) {
+        usec_t when;
+        int r;
+
+        assert(s);
+
+        if (!s->idle_event_source)
+                return 0;
+
+        r = sd_event_now(s->event, CLOCK_MONOTONIC, &when);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine current time: %m");
+
+        r = sd_event_source_set_time(s->idle_event_source, usec_add(when, IDLE_TIMEOUT_USEC));
+        if (r < 0)
+                return log_error_errno(r, "Failed to refresh idle timer: %m");
+
+        return 1;
+}
+
+static int set_namespace(Server *s, const char *namespace) {
+        assert(s);
+
+        if (!namespace)
+                return 0;
+
+        if (!log_namespace_name_valid(namespace))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified namespace name not valid, refusing: %s", namespace);
+
+        s->namespace = strdup(namespace);
+        if (!s->namespace)
+                return log_oom();
+
+        s->namespace_field = strjoin("_NAMESPACE=", namespace);
+        if (!s->namespace_field)
+                return log_oom();
+
+        return 1;
+}
+
+int server_init(Server *s, const char *namespace) {
+        const char *native_socket, *syslog_socket, *stdout_socket, *varlink_socket, *e;
         _cleanup_fdset_free_ FDSet *fds = NULL;
-        int n, r, fd;
+        int n, r, fd, varlink_fd = -1;
         bool no_sockets;
 
         assert(s);
@@ -2008,7 +2207,6 @@ int server_init(Server *s) {
                 .compress.enabled = true,
                 .compress.threshold_bytes = (uint64_t) -1,
                 .seal = true,
-                .read_kmsg = true,
 
                 .watchdog_usec = USEC_INFINITY,
 
@@ -2034,22 +2232,43 @@ int server_init(Server *s) {
                 .system_storage.name = "System Journal",
         };
 
+        r = set_namespace(s, namespace);
+        if (r < 0)
+                return r;
+
+        /* By default, only read from /dev/kmsg if are the main namespace */
+        s->read_kmsg = !s->namespace;
+        s->storage = s->namespace ? STORAGE_PERSISTENT : STORAGE_AUTO;
+
         journal_reset_metrics(&s->system_storage.metrics);
         journal_reset_metrics(&s->runtime_storage.metrics);
 
         server_parse_config_file(s);
 
-        r = proc_cmdline_parse(parse_proc_cmdline_item, s, PROC_CMDLINE_STRIP_RD_PREFIX);
-        if (r < 0)
-                log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
+        if (!s->namespace) {
+                /* Parse kernel command line, but only if we are not a namespace instance */
+                r = proc_cmdline_parse(parse_proc_cmdline_item, s, PROC_CMDLINE_STRIP_RD_PREFIX);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
+        }
 
-        if (!!s->ratelimit_interval ^ !!s->ratelimit_burst) {
+        if (!!s->ratelimit_interval != !!s->ratelimit_burst) { /* One set to 0 and the other not? */
                 log_debug("Setting both rate limit interval and burst from "USEC_FMT",%u to 0,0",
                           s->ratelimit_interval, s->ratelimit_burst);
                 s->ratelimit_interval = s->ratelimit_burst = 0;
         }
 
-        (void) mkdir_p("/run/systemd/journal", 0755);
+        e = getenv("RUNTIME_DIRECTORY");
+        if (e)
+                s->runtime_directory = strdup(e);
+        else if (s->namespace)
+                s->runtime_directory = strjoin("/run/systemd/journal.", s->namespace);
+        else
+                s->runtime_directory = strdup("/run/systemd/journal");
+        if (!s->runtime_directory)
+                return log_oom();
+
+        (void) mkdir_p(s->runtime_directory, 0755);
 
         s->user_journals = ordered_hashmap_new(NULL);
         if (!s->user_journals)
@@ -2071,9 +2290,14 @@ int server_init(Server *s) {
         if (n < 0)
                 return log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
 
+        native_socket = strjoina(s->runtime_directory, "/socket");
+        stdout_socket = strjoina(s->runtime_directory, "/stdout");
+        syslog_socket = strjoina(s->runtime_directory, "/dev-log");
+        varlink_socket = strjoina(s->runtime_directory, "/io.systemd.journal");
+
         for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
 
-                if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/run/systemd/journal/socket", 0) > 0) {
+                if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, native_socket, 0) > 0) {
 
                         if (s->native_fd >= 0)
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -2081,7 +2305,7 @@ int server_init(Server *s) {
 
                         s->native_fd = fd;
 
-                } else if (sd_is_socket_unix(fd, SOCK_STREAM, 1, "/run/systemd/journal/stdout", 0) > 0) {
+                } else if (sd_is_socket_unix(fd, SOCK_STREAM, 1, stdout_socket, 0) > 0) {
 
                         if (s->stdout_fd >= 0)
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -2089,8 +2313,7 @@ int server_init(Server *s) {
 
                         s->stdout_fd = fd;
 
-                } else if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/dev/log", 0) > 0 ||
-                           sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/run/systemd/journal/dev-log", 0) > 0) {
+                } else if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, syslog_socket, 0) > 0) {
 
                         if (s->syslog_fd >= 0)
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -2098,6 +2321,13 @@ int server_init(Server *s) {
 
                         s->syslog_fd = fd;
 
+                } else if (sd_is_socket_unix(fd, SOCK_STREAM, 1, varlink_socket, 0) > 0) {
+
+                        if (varlink_fd >= 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "Too many varlink sockets passed.");
+
+                        varlink_fd = fd;
                 } else if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1) > 0) {
 
                         if (s->audit_fd >= 0)
@@ -2128,22 +2358,22 @@ int server_init(Server *s) {
                 fds = fdset_free(fds);
         }
 
-        no_sockets = s->native_fd < 0 && s->stdout_fd < 0 && s->syslog_fd < 0 && s->audit_fd < 0;
+        no_sockets = s->native_fd < 0 && s->stdout_fd < 0 && s->syslog_fd < 0 && s->audit_fd < 0 && varlink_fd < 0;
 
         /* always open stdout, syslog, native, and kmsg sockets */
 
         /* systemd-journald.socket: /run/systemd/journal/stdout */
-        r = server_open_stdout_socket(s);
+        r = server_open_stdout_socket(s, stdout_socket);
         if (r < 0)
                 return r;
 
         /* systemd-journald-dev-log.socket: /run/systemd/journal/dev-log */
-        r = server_open_syslog_socket(s);
+        r = server_open_syslog_socket(s, syslog_socket);
         if (r < 0)
                 return r;
 
         /* systemd-journald.socket: /run/systemd/journal/socket */
-        r = server_open_native_socket(s);
+        r = server_open_native_socket(s, native_socket);
         if (r < 0)
                 return r;
 
@@ -2159,7 +2389,7 @@ int server_init(Server *s) {
                         return r;
         }
 
-        r = server_open_varlink(s);
+        r = server_open_varlink(s, varlink_socket, varlink_fd);
         if (r < 0)
                 return r;
 
@@ -2177,26 +2407,43 @@ int server_init(Server *s) {
 
         s->ratelimit = journal_ratelimit_new();
         if (!s->ratelimit)
-                return -ENOMEM;
+                return log_oom();
 
         r = cg_get_root_path(&s->cgroup_root);
         if (r < 0)
-                return r;
+                return log_error_errno(r, "Failed to acquire cgroup root path: %m");
 
         server_cache_hostname(s);
         server_cache_boot_id(s);
         server_cache_machine_id(s);
 
-        s->runtime_storage.path = path_join("/run/log/journal", SERVER_MACHINE_ID(s));
-        s->system_storage.path  = path_join("/var/log/journal", SERVER_MACHINE_ID(s));
-        if (!s->runtime_storage.path || !s->system_storage.path)
-                return -ENOMEM;
+        if (s->namespace)
+                s->runtime_storage.path = strjoin("/run/log/journal/", SERVER_MACHINE_ID(s), ".", s->namespace);
+        else
+                s->runtime_storage.path = strjoin("/run/log/journal/", SERVER_MACHINE_ID(s));
+        if (!s->runtime_storage.path)
+                return log_oom();
+
+        e = getenv("LOGS_DIRECTORY");
+        if (e)
+                s->system_storage.path = strdup(e);
+        else if (s->namespace)
+                s->system_storage.path = strjoin("/var/log/journal/", SERVER_MACHINE_ID(s), ".", s->namespace);
+        else
+                s->system_storage.path = strjoin("/var/log/journal/", SERVER_MACHINE_ID(s));
+        if (!s->system_storage.path)
+                return log_oom();
 
         (void) server_connect_notify(s);
 
         (void) client_context_acquire_default(s);
 
-        return system_journal_open(s, false, false);
+        r = system_journal_open(s, false, false);
+        if (r < 0)
+                return r;
+
+        server_start_or_stop_idle_timer(s);
+        return 0;
 }
 
 void server_maybe_append_tags(Server *s) {
@@ -2218,6 +2465,9 @@ void server_maybe_append_tags(Server *s) {
 void server_done(Server *s) {
         assert(s);
 
+        free(s->namespace);
+        free(s->namespace_field);
+
         set_free_with_destructor(s->deferred_closes, journal_file_close);
 
         while (s->stdout_streams)
@@ -2246,6 +2496,7 @@ void server_done(Server *s) {
         sd_event_source_unref(s->hostname_event_source);
         sd_event_source_unref(s->notify_event_source);
         sd_event_source_unref(s->watchdog_event_source);
+        sd_event_source_unref(s->idle_event_source);
         sd_event_unref(s->event);
 
         safe_close(s->syslog_fd);
@@ -2268,6 +2519,7 @@ void server_done(Server *s) {
         free(s->hostname_field);
         free(s->runtime_storage.path);
         free(s->system_storage.path);
+        free(s->runtime_directory);
 
         mmap_cache_unref(s->mmap);
 }
index a609e9db06d1fcad8c8c15b428546bf6f985f122..f3405e967cd0fbd357d7dc003722d461fd6ad6f7 100644 (file)
@@ -60,6 +60,8 @@ typedef struct JournalStorage {
 } JournalStorage;
 
 struct Server {
+        char *namespace;
+
         int syslog_fd;
         int native_fd;
         int stdout_fd;
@@ -84,6 +86,7 @@ struct Server {
         sd_event_source *hostname_event_source;
         sd_event_source *notify_event_source;
         sd_event_source *watchdog_event_source;
+        sd_event_source *idle_event_source;
 
         JournalFile *runtime_journal;
         JournalFile *system_journal;
@@ -147,6 +150,8 @@ struct Server {
         char machine_id_field[sizeof("_MACHINE_ID=") + 32];
         char boot_id_field[sizeof("_BOOT_ID=") + 32];
         char *hostname_field;
+        char *namespace_field;
+        char *runtime_directory;
 
         /* Cached cgroup root, so that we don't have to query that all the time */
         char *cgroup_root;
@@ -172,7 +177,7 @@ struct Server {
 #define SERVER_MACHINE_ID(s) ((s)->machine_id_field + STRLEN("_MACHINE_ID="))
 
 /* Extra fields for any log messages */
-#define N_IOVEC_META_FIELDS 22
+#define N_IOVEC_META_FIELDS 23
 
 /* Extra fields for log messages that contain OBJECT_PID= (i.e. log about another process) */
 #define N_IOVEC_OBJECT_FIELDS 18
@@ -204,7 +209,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_split_mode);
 const char *split_mode_to_string(SplitMode s) _const_;
 SplitMode split_mode_from_string(const char *s) _pure_;
 
-int server_init(Server *s);
+int server_init(Server *s, const char *namespace);
 void server_done(Server *s);
 void server_sync(Server *s);
 int server_vacuum(Server *s, bool verbose);
@@ -214,3 +219,6 @@ int server_flush_to_var(Server *s, bool require_flag_file);
 void server_maybe_append_tags(Server *s);
 int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userdata);
 void server_space_usage_message(Server *s, JournalStorage *storage);
+
+int server_start_or_stop_idle_timer(Server *s);
+int server_refresh_idle_timer(Server *s);
index 22a70ce5a69ff217098fa3b7bf20cd89730080c0..609af506a429d12ebca2e91bab88703485a940c7 100644 (file)
@@ -17,6 +17,7 @@
 #include "escape.h"
 #include "fd-util.h"
 #include "fileio.h"
+#include "fs-util.h"
 #include "io-util.h"
 #include "journald-console.h"
 #include "journald-context.h"
@@ -109,6 +110,8 @@ void stdout_stream_free(StdoutStream *s) {
 
                 if (s->in_notify_queue)
                         LIST_REMOVE(stdout_stream_notify_queue, s->server->stdout_streams_notify_queue, s);
+
+                (void) server_start_or_stop_idle_timer(s->server); /* Maybe we are idle now? */
         }
 
         if (s->event_source) {
@@ -139,7 +142,7 @@ void stdout_stream_destroy(StdoutStream *s) {
 }
 
 static int stdout_stream_save(StdoutStream *s) {
-        _cleanup_free_ char *temp_path = NULL;
+        _cleanup_(unlink_and_freep) char *temp_path = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         int r;
 
@@ -156,11 +159,12 @@ static int stdout_stream_save(StdoutStream *s) {
                         return log_warning_errno(errno, "Failed to stat connected stream: %m");
 
                 /* We use device and inode numbers as identifier for the stream */
-                if (asprintf(&s->state_file, "/run/systemd/journal/streams/%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino) < 0)
+                r = asprintf(&s->state_file, "%s/streams/%lu:%lu", s->server->runtime_directory, (unsigned long) st.st_dev, (unsigned long) st.st_ino);
+                if (r < 0)
                         return log_oom();
         }
 
-        (void) mkdir_p("/run/systemd/journal/streams", 0755);
+        (void) mkdir_parents(s->state_file, 0755);
 
         r = fopen_temporary(s->state_file, &f, &temp_path);
         if (r < 0)
@@ -214,6 +218,8 @@ static int stdout_stream_save(StdoutStream *s) {
                 goto fail;
         }
 
+        temp_path = mfree(temp_path);
+
         if (!s->fdstore && !s->in_notify_queue) {
                 LIST_PREPEND(stdout_stream_notify_queue, s->server->stdout_streams_notify_queue, s);
                 s->in_notify_queue = true;
@@ -229,10 +235,6 @@ static int stdout_stream_save(StdoutStream *s) {
 
 fail:
         (void) unlink(s->state_file);
-
-        if (temp_path)
-                (void) unlink(temp_path);
-
         return log_error_errno(r, "Failed to save stream data %s: %m", s->state_file);
 }
 
@@ -590,12 +592,14 @@ int stdout_stream_install(Server *s, int fd, StdoutStream **ret) {
         if (r < 0)
                 return log_error_errno(r, "Failed to generate stream ID: %m");
 
-        stream = new0(StdoutStream, 1);
+        stream = new(StdoutStream, 1);
         if (!stream)
                 return log_oom();
 
-        stream->fd = -1;
-        stream->priority = LOG_INFO;
+        *stream = (StdoutStream) {
+                .fd = -1,
+                .priority = LOG_INFO,
+        };
 
         xsprintf(stream->id_field, "_STREAM_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id));
 
@@ -629,11 +633,12 @@ int stdout_stream_install(Server *s, int fd, StdoutStream **ret) {
         LIST_PREPEND(stdout_stream, s->stdout_streams, stream);
         s->n_stdout_streams++;
 
+        (void) server_start_or_stop_idle_timer(s); /* Maybe no longer idle? */
+
         if (ret)
                 *ret = stream;
 
-        stream = NULL;
-
+        TAKE_PTR(stream);
         return 0;
 }
 
@@ -676,7 +681,7 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent
         if (r < 0)
                 return r;
 
-        fd = -1;
+        TAKE_FD(fd);
         return 0;
 }
 
@@ -694,7 +699,7 @@ static int stdout_stream_load(StdoutStream *stream, const char *fname) {
         assert(fname);
 
         if (!stream->state_file) {
-                stream->state_file = path_join("/run/systemd/journal/streams", fname);
+                stream->state_file = path_join(stream->server->runtime_directory, "streams", fname);
                 if (!stream->state_file)
                         return log_oom();
         }
@@ -783,14 +788,16 @@ static int stdout_stream_restore(Server *s, const char *fname, int fd) {
 int server_restore_streams(Server *s, FDSet *fds) {
         _cleanup_closedir_ DIR *d = NULL;
         struct dirent *de;
+        const char *path;
         int r;
 
-        d = opendir("/run/systemd/journal/streams");
+        path = strjoina(s->runtime_directory, "/streams");
+        d = opendir(path);
         if (!d) {
                 if (errno == ENOENT)
                         return 0;
 
-                return log_warning_errno(errno, "Failed to enumerate /run/systemd/journal/streams: %m");
+                return log_warning_errno(errno, "Failed to enumerate %s: %m", path);
         }
 
         FOREACH_DIRENT(de, d, goto fail) {
@@ -818,8 +825,7 @@ int server_restore_streams(Server *s, FDSet *fds) {
                         /* No file descriptor? Then let's delete the state file */
                         log_debug("Cannot restore stream file %s", de->d_name);
                         if (unlinkat(dirfd(d), de->d_name, 0) < 0)
-                                log_warning_errno(errno, "Failed to remove /run/systemd/journal/streams/%s: %m",
-                                                  de->d_name);
+                                log_warning_errno(errno, "Failed to remove %s/%s: %m", path, de->d_name);
                         continue;
                 }
 
@@ -836,23 +842,28 @@ fail:
         return log_error_errno(errno, "Failed to read streams directory: %m");
 }
 
-int server_open_stdout_socket(Server *s) {
-        static const union sockaddr_union sa = {
-                .un.sun_family = AF_UNIX,
-                .un.sun_path = "/run/systemd/journal/stdout",
-        };
+int server_open_stdout_socket(Server *s, const char *stdout_socket) {
         int r;
 
         assert(s);
+        assert(stdout_socket);
 
         if (s->stdout_fd < 0) {
+                union sockaddr_union sa;
+                socklen_t sa_len;
+
+                r = sockaddr_un_set_path(&sa.un, stdout_socket);
+                if (r < 0)
+                        return log_error_errno(r, "Unable to use namespace path %s for AF_UNIX socket: %m", stdout_socket);
+                sa_len = r;
+
                 s->stdout_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
                 if (s->stdout_fd < 0)
                         return log_error_errno(errno, "socket() failed: %m");
 
                 (void) sockaddr_un_unlink(&sa.un);
 
-                r = bind(s->stdout_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
+                r = bind(s->stdout_fd, &sa.sa, sa_len);
                 if (r < 0)
                         return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
 
index 487376e76361221cdef167f549e7831799ee9810..7ab0016618489cb53f19bad94dc9014d39b9414e 100644 (file)
@@ -6,7 +6,7 @@ typedef struct StdoutStream StdoutStream;
 #include "fdset.h"
 #include "journald-server.h"
 
-int server_open_stdout_socket(Server *s);
+int server_open_stdout_socket(Server *s, const char *stdout_socket);
 int server_restore_streams(Server *s, FDSet *fds);
 
 void stdout_stream_free(StdoutStream *s);
index a60a259bc49e587d5b1f2c3aba3708dea906a307..1325b563854f6f5da2f9b41def2dc3abdb7712c6 100644 (file)
 /* Warn once every 30s if we missed syslog message */
 #define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
 
-static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, const struct ucred *ucred, const struct timeval *tv) {
+static void forward_syslog_iovec(
+                Server *s,
+                const struct iovec *iovec,
+                unsigned n_iovec,
+                const struct ucred *ucred,
+                const struct timeval *tv) {
+
+        union sockaddr_union sa;
 
-        static const union sockaddr_union sa = {
-                .un.sun_family = AF_UNIX,
-                .un.sun_path = "/run/systemd/journal/syslog",
-        };
         struct msghdr msghdr = {
                 .msg_iov = (struct iovec *) iovec,
                 .msg_iovlen = n_iovec,
-                .msg_name = (struct sockaddr*) &sa.sa,
-                .msg_namelen = SOCKADDR_UN_LEN(sa.un),
         };
         struct cmsghdr *cmsg;
         union {
                 struct cmsghdr cmsghdr;
                 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
         } control;
+        const char *j;
+        int r;
 
         assert(s);
         assert(iovec);
         assert(n_iovec > 0);
 
+        j = strjoina(s->runtime_directory, "/syslog");
+        r = sockaddr_un_set_path(&sa.un, j);
+        if (r < 0) {
+                log_debug_errno(r, "Forwarding socket path %s too long for AF_UNIX, not forwarding: %m", j);
+                return;
+        }
+
+        msghdr.msg_name = &sa.sa;
+        msghdr.msg_namelen = r;
+
         if (ucred) {
                 zero(control);
                 msghdr.msg_control = &control;
@@ -441,24 +454,28 @@ void server_process_syslog_message(
         server_dispatch_message(s, iovec, n, m, context, tv, priority, 0);
 }
 
-int server_open_syslog_socket(Server *s) {
-
-        static const union sockaddr_union sa = {
-                .un.sun_family = AF_UNIX,
-                .un.sun_path = "/run/systemd/journal/dev-log",
-        };
+int server_open_syslog_socket(Server *s, const char *syslog_socket) {
         int r;
 
         assert(s);
+        assert(syslog_socket);
 
         if (s->syslog_fd < 0) {
+                union sockaddr_union sa;
+                socklen_t sa_len;
+
+                r = sockaddr_un_set_path(&sa.un, syslog_socket);
+                if (r < 0)
+                        return log_error_errno(r, "Unable to use namespace path %s for AF_UNIX socket: %m", syslog_socket);
+                sa_len = r;
+
                 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
                 if (s->syslog_fd < 0)
                         return log_error_errno(errno, "socket() failed: %m");
 
                 (void) sockaddr_un_unlink(&sa.un);
 
-                r = bind(s->syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
+                r = bind(s->syslog_fd, &sa.sa, sa_len);
                 if (r < 0)
                         return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
 
index 7ca58972649b69ecbd6374386c7808d7f8d27faf..5ad601001ca3e8f27ea7dd3946932708b2c26de3 100644 (file)
@@ -10,6 +10,6 @@ size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid);
 void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv);
 
 void server_process_syslog_message(Server *s, const char *buf, size_t buf_len, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len);
-int server_open_syslog_socket(Server *s);
+int server_open_syslog_socket(Server *s, const char *syslog_socket);
 
 void server_maybe_warn_forward_syslog_missed(Server *s);
index 5e7b1dcb4ac3476067e2abc599a5f361970ae94c..9a7cb3e1fc457aa89da9e41ac8466b54fe51b484 100644 (file)
 #include "sigbus.h"
 
 int main(int argc, char *argv[]) {
+        const char *namespace;
         Server server;
         int r;
 
-        if (argc > 1) {
-                log_error("This program does not take arguments.");
+        if (argc > 2) {
+                log_error("This program takes one or no arguments.");
                 return EXIT_FAILURE;
         }
 
+        namespace = argc > 1 ? empty_to_null(argv[1]) : NULL;
+
         log_set_prohibit_ipc(true);
         log_set_target(LOG_TARGET_AUTO);
         log_set_facility(LOG_SYSLOG);
@@ -32,7 +35,7 @@ int main(int argc, char *argv[]) {
 
         sigbus_install();
 
-        r = server_init(&server);
+        r = server_init(&server, namespace);
         if (r < 0)
                 goto finish;
 
@@ -40,7 +43,11 @@ int main(int argc, char *argv[]) {
         server_flush_to_var(&server, true);
         server_flush_dev_kmsg(&server);
 
-        log_debug("systemd-journald running as pid "PID_FMT, getpid_cached());
+        if (server.namespace)
+                log_debug("systemd-journald running as PID "PID_FMT" for namespace '%s'.", getpid_cached(), server.namespace);
+        else
+                log_debug("systemd-journald running as PID "PID_FMT" for the system.", getpid_cached());
+
         server_driver_message(&server, 0,
                               "MESSAGE_ID=" SD_MESSAGE_JOURNAL_START_STR,
                               LOG_MESSAGE("Journal started"),
@@ -55,8 +62,10 @@ int main(int argc, char *argv[]) {
                 usec_t t = USEC_INFINITY, n;
 
                 r = sd_event_get_state(server.event);
-                if (r < 0)
+                if (r < 0) {
+                        log_error_errno(r, "Failed to get event loop state: %m");
                         goto finish;
+                }
                 if (r == SD_EVENT_FINISHED)
                         break;
 
@@ -99,7 +108,11 @@ int main(int argc, char *argv[]) {
                 server_maybe_warn_forward_syslog_missed(&server);
         }
 
-        log_debug("systemd-journald stopped as pid "PID_FMT, getpid_cached());
+        if (server.namespace)
+                log_debug("systemd-journald stopped as PID "PID_FMT" for namespace '%s'.", getpid_cached(), server.namespace);
+        else
+                log_debug("systemd-journald stopped as PID "PID_FMT" for the system.", getpid_cached());
+
         server_driver_message(&server, 0,
                               "MESSAGE_ID=" SD_MESSAGE_JOURNAL_STOP_STR,
                               LOG_MESSAGE("Journal stopped"),
index 358f2fd738ef9aa2be9f9fbc5ed002f95e46940f..17998090f431a744de45ef3203ad7f581bb454aa 100644 (file)
@@ -39,6 +39,7 @@
 #include "stdio-util.h"
 #include "string-util.h"
 #include "strv.h"
+#include "syslog-util.h"
 
 #define JOURNAL_FILES_MAX 7168
 
@@ -161,7 +162,7 @@ static int match_is_valid(const void *data, size_t size) {
         if (size < 2)
                 return false;
 
-        if (startswith(data, "__"))
+        if (((char*) data)[0] == '_' && ((char*) data)[1] == '_')
                 return false;
 
         b = data;
@@ -204,16 +205,17 @@ static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
 static Match *match_new(Match *p, MatchType t) {
         Match *m;
 
-        m = new0(Match, 1);
+        m = new(Match, 1);
         if (!m)
                 return NULL;
 
-        m->type = t;
+        *m = (Match) {
+                .type = t,
+                .parent = p,
+        };
 
-        if (p) {
-                m->parent = p;
+        if (p)
                 LIST_PREPEND(matches, p->matches, m);
-        }
 
         return m;
 }
@@ -433,11 +435,12 @@ _public_ void sd_journal_flush_matches(sd_journal *j) {
         detach_location(j);
 }
 
-_pure_ static int compare_with_location(JournalFile *f, Location *l) {
+_pure_ static int compare_with_location(const JournalFile *f, const Location *l, const JournalFile *current_file) {
         int r;
 
         assert(f);
         assert(l);
+        assert(current_file);
         assert(f->location_type == LOCATION_SEEK);
         assert(IN_SET(l->type, LOCATION_DISCRETE, LOCATION_SEEK));
 
@@ -446,7 +449,8 @@ _pure_ static int compare_with_location(JournalFile *f, Location *l) {
             l->realtime_set &&
             f->current_realtime == l->realtime &&
             l->xor_hash_set &&
-            f->current_xor_hash == l->xor_hash)
+            f->current_xor_hash == l->xor_hash &&
+            f != current_file)
                 return 0;
 
         if (l->seqnum_set &&
@@ -785,7 +789,7 @@ static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direc
                 if (j->current_location.type == LOCATION_DISCRETE) {
                         int k;
 
-                        k = compare_with_location(f, &j->current_location);
+                        k = compare_with_location(f, &j->current_location, j->current_file);
 
                         found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
                 } else
@@ -1434,22 +1438,63 @@ static void remove_file_real(sd_journal *j, JournalFile *f) {
 
 static int dirname_is_machine_id(const char *fn) {
         sd_id128_t id, machine;
+        const char *e;
         int r;
 
+        /* Returns true if the specified directory name matches the local machine ID */
+
         r = sd_id128_get_machine(&machine);
         if (r < 0)
                 return r;
 
-        r = sd_id128_from_string(fn, &id);
+        e = strchr(fn, '.');
+        if (e) {
+                const char *k;
+
+                /* Looks like it has a namespace suffix. Verify that. */
+                if (!log_namespace_name_valid(e + 1))
+                        return false;
+
+                k = strndupa(fn, e - fn);
+                r = sd_id128_from_string(k, &id);
+        } else
+                r = sd_id128_from_string(fn, &id);
         if (r < 0)
                 return r;
 
         return sd_id128_equal(id, machine);
 }
 
+static int dirname_has_namespace(const char *fn, const char *namespace) {
+        const char *e;
+
+        /* Returns true if the specified directory name matches the specified namespace */
+
+        e = strchr(fn, '.');
+        if (e) {
+                const char *k;
+
+                if (!namespace)
+                        return false;
+
+                if (!streq(e + 1, namespace))
+                        return false;
+
+                k = strndupa(fn, e - fn);
+                return id128_is_valid(k);
+        }
+
+        if (namespace)
+                return false;
+
+        return id128_is_valid(fn);
+}
+
 static bool dirent_is_journal_file(const struct dirent *de) {
         assert(de);
 
+        /* Returns true if the specified directory entry looks like a journal file we might be interested in */
+
         if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN))
                 return false;
 
@@ -1457,13 +1502,26 @@ static bool dirent_is_journal_file(const struct dirent *de) {
                 endswith(de->d_name, ".journal~");
 }
 
-static bool dirent_is_id128_subdir(const struct dirent *de) {
+static bool dirent_is_journal_subdir(const struct dirent *de) {
+        const char *e, *n;
         assert(de);
 
+        /* returns true if the specified directory entry looks like a directory that might contain journal
+         * files we might be interested in, i.e. is either a 128bit ID or a 128bit ID suffixed by a
+         * namespace. */
+
         if (!IN_SET(de->d_type, DT_DIR, DT_LNK, DT_UNKNOWN))
                 return false;
 
-        return id128_is_valid(de->d_name);
+        e = strchr(de->d_name, '.');
+        if (!e)
+                return id128_is_valid(de->d_name); /* No namespace */
+
+        n = strndupa(de->d_name, e - de->d_name);
+        if (!id128_is_valid(n))
+                return false;
+
+        return log_namespace_name_valid(e + 1);
 }
 
 static int directory_open(sd_journal *j, const char *path, DIR **ret) {
@@ -1500,7 +1558,7 @@ static void directory_enumerate(sd_journal *j, Directory *m, DIR *d) {
                 if (dirent_is_journal_file(de))
                         (void) add_file_by_name(j, m->path, de->d_name);
 
-                if (m->is_root && dirent_is_id128_subdir(de))
+                if (m->is_root && dirent_is_journal_subdir(de))
                         (void) add_directory(j, m->path, de->d_name);
         }
 
@@ -1540,7 +1598,11 @@ static void directory_watch(sd_journal *j, Directory *m, int fd, uint32_t mask)
         }
 }
 
-static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
+static int add_directory(
+                sd_journal *j,
+                const char *prefix,
+                const char *dirname) {
+
         _cleanup_free_ char *path = NULL;
         _cleanup_closedir_ DIR *d = NULL;
         Directory *m;
@@ -1565,6 +1627,11 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
             !((dirname && dirname_is_machine_id(dirname) > 0) || path_has_prefix(j, path, "/run")))
                 return 0;
 
+        if (!(FLAGS_SET(j->flags, SD_JOURNAL_ALL_NAMESPACES) ||
+              dirname_has_namespace(dirname, j->namespace) > 0 ||
+              (FLAGS_SET(j->flags, SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE) && dirname_has_namespace(dirname, NULL) > 0)))
+                return 0;
+
         r = directory_open(j, path, &d);
         if (r < 0) {
                 log_debug_errno(r, "Failed to open directory '%s': %m", path);
@@ -1573,14 +1640,16 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
 
         m = hashmap_get(j->directories_by_path, path);
         if (!m) {
-                m = new0(Directory, 1);
+                m = new(Directory, 1);
                 if (!m) {
                         r = -ENOMEM;
                         goto fail;
                 }
 
-                m->is_root = false;
-                m->path = path;
+                *m = (Directory) {
+                        .is_root = false,
+                        .path = path,
+                };
 
                 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
                         free(m);
@@ -1803,7 +1872,7 @@ static int allocate_inotify(sd_journal *j) {
         return hashmap_ensure_allocated(&j->directories_by_wd, NULL);
 }
 
-static sd_journal *journal_new(int flags, const char *path) {
+static sd_journal *journal_new(int flags, const char *path, const char *namespace) {
         _cleanup_(sd_journal_closep) sd_journal *j = NULL;
 
         j = new0(sd_journal, 1);
@@ -1829,6 +1898,12 @@ static sd_journal *journal_new(int flags, const char *path) {
                         j->path = t;
         }
 
+        if (namespace) {
+                j->namespace = strdup(namespace);
+                if (!j->namespace)
+                        return NULL;
+        }
+
         j->files = ordered_hashmap_new(&path_hash_ops);
         if (!j->files)
                 return NULL;
@@ -1845,16 +1920,19 @@ static sd_journal *journal_new(int flags, const char *path) {
 #define OPEN_ALLOWED_FLAGS                              \
         (SD_JOURNAL_LOCAL_ONLY |                        \
          SD_JOURNAL_RUNTIME_ONLY |                      \
-         SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)
+         SD_JOURNAL_SYSTEM |                            \
+         SD_JOURNAL_CURRENT_USER |                      \
+         SD_JOURNAL_ALL_NAMESPACES |                    \
+         SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE)
 
-_public_ int sd_journal_open(sd_journal **ret, int flags) {
+_public_ int sd_journal_open_namespace(sd_journal **ret, const char *namespace, int flags) {
         _cleanup_(sd_journal_closep) sd_journal *j = NULL;
         int r;
 
         assert_return(ret, -EINVAL);
         assert_return((flags & ~OPEN_ALLOWED_FLAGS) == 0, -EINVAL);
 
-        j = journal_new(flags, NULL);
+        j = journal_new(flags, NULL, namespace);
         if (!j)
                 return -ENOMEM;
 
@@ -1866,6 +1944,10 @@ _public_ int sd_journal_open(sd_journal **ret, int flags) {
         return 0;
 }
 
+_public_ int sd_journal_open(sd_journal **ret, int flags) {
+        return sd_journal_open_namespace(ret, NULL, flags);
+}
+
 #define OPEN_CONTAINER_ALLOWED_FLAGS                    \
         (SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_SYSTEM)
 
@@ -1875,7 +1957,7 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in
         char *p;
         int r;
 
-        /* This is pretty much deprecated, people should use machined's OpenMachineRootDirectory() call instead in
+        /* This is deprecated, people should use machined's OpenMachineRootDirectory() call instead in
          * combination with sd_journal_open_directory_fd(). */
 
         assert_return(machine, -EINVAL);
@@ -1897,7 +1979,7 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in
         if (!streq_ptr(class, "container"))
                 return -EIO;
 
-        j = journal_new(flags, root);
+        j = journal_new(flags, root, NULL);
         if (!j)
                 return -ENOMEM;
 
@@ -1921,7 +2003,7 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f
         assert_return(path, -EINVAL);
         assert_return((flags & ~OPEN_DIRECTORY_ALLOWED_FLAGS) == 0, -EINVAL);
 
-        j = journal_new(flags, path);
+        j = journal_new(flags, path, NULL);
         if (!j)
                 return -ENOMEM;
 
@@ -1944,7 +2026,7 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla
         assert_return(ret, -EINVAL);
         assert_return(flags == 0, -EINVAL);
 
-        j = journal_new(flags, NULL);
+        j = journal_new(flags, NULL, NULL);
         if (!j)
                 return -ENOMEM;
 
@@ -1979,7 +2061,7 @@ _public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) {
         if (!S_ISDIR(st.st_mode))
                 return -EBADFD;
 
-        j = journal_new(flags, NULL);
+        j = journal_new(flags, NULL, NULL);
         if (!j)
                 return -ENOMEM;
 
@@ -2007,7 +2089,7 @@ _public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fd
         assert_return(n_fds > 0, -EBADF);
         assert_return(flags == 0, -EINVAL);
 
-        j = journal_new(flags, NULL);
+        j = journal_new(flags, NULL, NULL);
         if (!j)
                 return -ENOMEM;
 
@@ -2079,6 +2161,7 @@ _public_ void sd_journal_close(sd_journal *j) {
 
         free(j->path);
         free(j->prefix);
+        free(j->namespace);
         free(j->unique_field);
         free(j->fields_buffer);
         free(j);
@@ -2480,7 +2563,7 @@ static void process_q_overflow(sd_journal *j) {
         log_debug("Reiteration complete.");
 }
 
-static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
+static void process_inotify_event(sd_journal *j, const struct inotify_event *e) {
         Directory *d;
 
         assert(j);
@@ -2580,6 +2663,8 @@ _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
         assert_return(!journal_pid_changed(j), -ECHILD);
 
         if (j->inotify_fd < 0) {
+                Iterator i;
+                JournalFile *f;
 
                 /* This is the first invocation, hence create the
                  * inotify watch */
@@ -2587,6 +2672,19 @@ _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
                 if (r < 0)
                         return r;
 
+                /* Server might have done some vacuuming while we weren't watching.
+                   Get rid of the deleted files now so they don't stay around indefinitely. */
+                ORDERED_HASHMAP_FOREACH(f, j->files, i) {
+                        r = journal_file_fstat(f);
+                        if (r < 0) {
+                                log_debug_errno(r,"Failed to fstat() journal file '%s' : %m", f->path);
+                                continue;
+                        }
+
+                        if (f->last_stat.st_nlink <= 0)
+                                remove_file_real(j, f);
+                }
+
                 /* The journal might have changed since the context
                  * object was created and we weren't watching before,
                  * hence don't wait for anything, and return
index 2aa8c583196aceff1f0dcacc793ef0be9d417e76..21c09fa69179ff301b8ae3ee65480a2ea156f648 100644 (file)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/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
 
index 268b6ce0a5266dc6e1ab192d438f967f3f1cad1c..3850eacef5cebd00e4014bd66ff00e40564f69bf 100644 (file)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/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
 
index 47a0cd224ae8518ba56732305604eec3c521d462..6c0e27ba3a6ef991600bf36e65b27916c1142589 100644 (file)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/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
 
@@ -55,6 +55,7 @@ else
     read -r -d '' -a line < /proc/cmdline
     for i in "${line[@]}"; do
         [[ "${i#initrd=*}" != "$i" ]] && continue
+        [[ "${i#BOOT_IMAGE=*}" != "$i" ]] && continue
         BOOT_OPTIONS+=("$i")
     done
 fi
index e7f2beb570792a00088d458f043d736553f47a68..4812464da9ac17f81164ffe4c34e207f46eb2bb2 100644 (file)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/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
 # SPDX-License-Identifier: LGPL-2.1+
index 0bf0b0e5526073196eaff3145bc9d7a485a5d075..13a0a5d92957602f63b1260ed10509dbde473c63 100644 (file)
@@ -8,6 +8,7 @@
 #include "sd-ndisc.h"
 
 #include "alloc-util.h"
+#include "arphrd-list.h"
 #include "condition.h"
 #include "conf-parser.h"
 #include "device-util.h"
@@ -166,6 +167,27 @@ static const char *const wifi_iftype_table[NL80211_IFTYPE_MAX+1] = {
 
 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(wifi_iftype, enum nl80211_iftype);
 
+char *link_get_type_string(unsigned short iftype, sd_device *device) {
+        const char *t, *devtype;
+        char *p;
+
+        if (device &&
+            sd_device_get_devtype(device, &devtype) >= 0 &&
+            !isempty(devtype))
+                return strdup(devtype);
+
+        t = arphrd_to_name(iftype);
+        if (!t)
+                return NULL;
+
+        p = strdup(t);
+        if (!p)
+                return NULL;
+
+        ascii_strlower(p);
+        return p;
+}
+
 bool net_match_config(Set *match_mac,
                       Set *match_permanent_mac,
                       char * const *match_paths,
@@ -176,6 +198,7 @@ bool net_match_config(Set *match_mac,
                       char * const *match_wifi_iftype,
                       char * const *match_ssid,
                       Set *match_bssid,
+                      unsigned short iftype,
                       sd_device *device,
                       const struct ether_addr *dev_mac,
                       const struct ether_addr *dev_permanent_mac,
@@ -185,13 +208,14 @@ bool net_match_config(Set *match_mac,
                       const char *ssid,
                       const struct ether_addr *bssid) {
 
-        const char *dev_path = NULL, *dev_driver = NULL, *dev_type = NULL, *mac_str;
+        const char *dev_path = NULL, *dev_driver = NULL, *mac_str;
+        _cleanup_free_ char *dev_type;
+
+        dev_type = link_get_type_string(iftype, device);
 
         if (device) {
                 (void) sd_device_get_property_value(device, "ID_PATH", &dev_path);
                 (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &dev_driver);
-                (void) sd_device_get_devtype(device, &dev_type);
-
                 if (!dev_name)
                         (void) sd_device_get_sysname(device, &dev_name);
                 if (!dev_mac &&
index dff6c8831a03679458ff8c1ee8d7bd61ee977c9b..593bad223068647abd5320ffcff41475227b88bc 100644 (file)
@@ -15,6 +15,7 @@
 #define LINK_BRIDGE_PORT_PRIORITY_INVALID 128
 #define LINK_BRIDGE_PORT_PRIORITY_MAX 63
 
+char *link_get_type_string(unsigned short iftype, sd_device *device);
 bool net_match_config(Set *match_mac,
                       Set *match_permanent_mac,
                       char * const *match_path,
@@ -25,6 +26,7 @@ bool net_match_config(Set *match_mac,
                       char * const *match_wifi_iftype,
                       char * const *match_ssid,
                       Set *match_bssid,
+                      unsigned short iftype,
                       sd_device *device,
                       const struct ether_addr *dev_mac,
                       const struct ether_addr *dev_permanent_mac,
index 5417ba8c5febc40265c24a97f4347026f1899dcb..eac2e725cce7b4a8e66ff595f2a2301ef9237361 100644 (file)
@@ -675,8 +675,7 @@ static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void
 }
 
 static usec_t client_timeout_compute_random(usec_t val) {
-        return val - val / 10 +
-                (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
+        return val - (random_u32() % USEC_PER_SEC) * val / 10 / USEC_PER_SEC;
 }
 
 static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) {
@@ -686,7 +685,6 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
         usec_t max_retransmit_duration = 0;
         uint8_t max_retransmit_count = 0;
         char time_string[FORMAT_TIMESPAN_MAX];
-        uint32_t expire = 0;
 
         assert(s);
         assert(client);
@@ -735,8 +733,9 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
                 max_retransmit_time = DHCP6_REB_MAX_RT;
 
                 if (event_source_is_enabled(client->timeout_resend_expire) <= 0) {
-                        r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
-                                                         &expire);
+                        uint32_t expire = 0;
+
+                        r = dhcp6_lease_ia_rebind_expire(&client->lease->ia, &expire);
                         if (r < 0) {
                                 client_stop(client, r);
                                 return 0;
@@ -751,7 +750,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
                 return 0;
         }
 
-        if (max_retransmit_count &&
+        if (max_retransmit_count > 0 &&
             client->retransmit_count >= max_retransmit_count) {
                 client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX);
                 return 0;
@@ -765,7 +764,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
         if (r >= 0)
                 client->retransmit_count++;
 
-        if (!client->retransmit_time) {
+        if (client->retransmit_time == 0) {
                 client->retransmit_time =
                         client_timeout_compute_random(init_retransmit_time);
 
@@ -773,7 +772,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
                         client->retransmit_time += init_retransmit_time / 10;
 
         } else {
-                if (max_retransmit_time &&
+                if (max_retransmit_time > 0 &&
                     client->retransmit_time > max_retransmit_time / 2)
                         client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
                 else
@@ -791,7 +790,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
         if (r < 0)
                 goto error;
 
-        if (max_retransmit_duration && event_source_is_enabled(client->timeout_resend_expire) <= 0) {
+        if (max_retransmit_duration > 0 && event_source_is_enabled(client->timeout_resend_expire) <= 0) {
 
                 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
                                  max_retransmit_duration / USEC_PER_SEC);
index 873a2f40f84363182fb8ef066bd908c7455b9c5e..e7ffe879a82ac7da212fb34782f69842f8d59dc2 100644 (file)
@@ -829,6 +829,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 a3dd9f68b7d38c0a1b6da69c25b8261f88a0d6d9..8b6ebbcf8bf6ca42369ef576d68a86a3295d134e 100644 (file)
@@ -685,6 +685,8 @@ global:
 
 LIBSYSTEMD_245 {
 global:
+        sd_bus_enqueue_for_read;
+        sd_bus_message_dump;
         sd_bus_message_sensitive;
         sd_event_add_child_pidfd;
         sd_event_source_get_child_pidfd;
@@ -693,4 +695,5 @@ global:
         sd_event_source_get_child_process_own;
         sd_event_source_set_child_process_own;
         sd_event_source_send_child_signal;
+        sd_journal_open_namespace;
 } LIBSYSTEMD_243;
index 4e23edd923273ea4cf48968ca8d2bb73e4cfb8c8..174f1228af29fdc35cdbd2c14322de7543fb105f 100644 (file)
@@ -105,5 +105,35 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
         SD_BUS_ERROR_MAP(BUS_ERROR_SPEED_METER_INACTIVE,         EOPNOTSUPP),
         SD_BUS_ERROR_MAP(BUS_ERROR_UNMANAGED_INTERFACE,          EOPNOTSUPP),
 
+        SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_HOME,                 EEXIST),
+        SD_BUS_ERROR_MAP(BUS_ERROR_UID_IN_USE,                   EEXIST),
+        SD_BUS_ERROR_MAP(BUS_ERROR_USER_NAME_EXISTS,             EEXIST),
+        SD_BUS_ERROR_MAP(BUS_ERROR_HOME_EXISTS,                  EEXIST),
+        SD_BUS_ERROR_MAP(BUS_ERROR_HOME_ALREADY_ACTIVE,          EALREADY),
+        SD_BUS_ERROR_MAP(BUS_ERROR_HOME_ALREADY_FIXATED,         EALREADY),
+        SD_BUS_ERROR_MAP(BUS_ERROR_HOME_UNFIXATED,               EADDRNOTAVAIL),
+        SD_BUS_ERROR_MAP(BUS_ERROR_HOME_NOT_ACTIVE,              EALREADY),
+        SD_BUS_ERROR_MAP(BUS_ERROR_HOME_ABSENT,                  EREMOTE),
+        SD_BUS_ERROR_MAP(BUS_ERROR_HOME_BUSY,                    EBUSY),
+        SD_BUS_ERROR_MAP(BUS_ERROR_BAD_PASSWORD,                 ENOKEY),
+        SD_BUS_ERROR_MAP(BUS_ERROR_LOW_PASSWORD_QUALITY,         EUCLEAN),
+        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_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),
+        SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT,   EUCLEAN),
+        SD_BUS_ERROR_MAP(BUS_ERROR_BAD_SIGNATURE,                EKEYREJECTED),
+        SD_BUS_ERROR_MAP(BUS_ERROR_HOME_RECORD_MISMATCH,         EUCLEAN),
+        SD_BUS_ERROR_MAP(BUS_ERROR_HOME_RECORD_DOWNGRADE,        ESTALE),
+        SD_BUS_ERROR_MAP(BUS_ERROR_HOME_RECORD_SIGNED,           EROFS),
+        SD_BUS_ERROR_MAP(BUS_ERROR_BAD_HOME_SIZE,                ERANGE),
+        SD_BUS_ERROR_MAP(BUS_ERROR_NO_PRIVATE_KEY,               ENOPKG),
+        SD_BUS_ERROR_MAP(BUS_ERROR_HOME_LOCKED,                  ENOEXEC),
+        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_END
 };
index 8da56551f697111b0a64a0e29509a4026ea30a94..e5f92b9ec261e3c669cf502348ffc6b2625a9967 100644 (file)
 #define BUS_ERROR_SPEED_METER_INACTIVE "org.freedesktop.network1.SpeedMeterInactive"
 #define BUS_ERROR_UNMANAGED_INTERFACE "org.freedesktop.network1.UnmanagedInterface"
 
+#define BUS_ERROR_NO_SUCH_HOME "org.freedesktop.home1.NoSuchHome"
+#define BUS_ERROR_UID_IN_USE "org.freedesktop.home1.UIDInUse"
+#define BUS_ERROR_USER_NAME_EXISTS "org.freedesktop.home1.UserNameExists"
+#define BUS_ERROR_HOME_EXISTS "org.freedesktop.home1.HomeExists"
+#define BUS_ERROR_HOME_ALREADY_ACTIVE "org.freedesktop.home1.HomeAlreadyActive"
+#define BUS_ERROR_HOME_ALREADY_FIXATED "org.freedesktop.home1.HomeAlreadyFixated"
+#define BUS_ERROR_HOME_UNFIXATED "org.freedesktop.home1.HomeUnfixated"
+#define BUS_ERROR_HOME_NOT_ACTIVE "org.freedesktop.home1.HomeNotActive"
+#define BUS_ERROR_HOME_ABSENT "org.freedesktop.home1.HomeAbsent"
+#define BUS_ERROR_HOME_BUSY "org.freedesktop.home1.HomeBusy"
+#define BUS_ERROR_BAD_PASSWORD "org.freedesktop.home1.BadPassword"
+#define BUS_ERROR_LOW_PASSWORD_QUALITY "org.freedesktop.home1.LowPasswordQuality"
+#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_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_TOKEN_BAD_PIN_ONE_TRY_LEFT "org.freedesktop.home1.BadPinOneTryLeft"
+#define BUS_ERROR_BAD_SIGNATURE "org.freedesktop.home1.BadSignature"
+#define BUS_ERROR_HOME_RECORD_MISMATCH "org.freedesktop.home1.RecordMismatch"
+#define BUS_ERROR_HOME_RECORD_DOWNGRADE "org.freedesktop.home1.RecordDowngrade"
+#define BUS_ERROR_HOME_RECORD_SIGNED "org.freedesktop.home1.RecordSigned"
+#define BUS_ERROR_BAD_HOME_SIZE "org.freedesktop.home1.BadHomeSize"
+#define BUS_ERROR_NO_PRIVATE_KEY "org.freedesktop.home1.NoPrivateKey"
+#define BUS_ERROR_HOME_LOCKED "org.freedesktop.home1.HomeLocked"
+#define BUS_ERROR_HOME_NOT_LOCKED "org.freedesktop.home1.HomeNotLocked"
+#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"
+
 BUS_ERROR_MAP_ELF_USE(bus_common_errors);
index 9a6a81d7aaa5c18e2c6da5e62775ccba62248af0..caab5e5ebe65c873649021dbccd0addb55ae86f2 100644 (file)
 #include "terminal-util.h"
 #include "util.h"
 
-static char *indent(unsigned level, unsigned flags) {
+static char *indent(unsigned level, uint64_t flags) {
         char *p;
         unsigned n, i = 0;
 
         n = 0;
 
-        if (flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY && level > 0)
+        if (flags & SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY && level > 0)
                 level -= 1;
 
-        if (flags & BUS_MESSAGE_DUMP_WITH_HEADER)
+        if (flags & SD_BUS_MESSAGE_DUMP_WITH_HEADER)
                 n += 2;
 
         p = new(char, n + level*8 + 1);
         if (!p)
                 return NULL;
 
-        if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) {
+        if (flags & SD_BUS_MESSAGE_DUMP_WITH_HEADER) {
                 p[i++] = ' ';
                 p[i++] = ' ';
         }
@@ -45,7 +45,7 @@ static char *indent(unsigned level, unsigned flags) {
         return p;
 }
 
-int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags) {
+_public_ int sd_bus_message_dump(sd_bus_message *m, FILE *f, uint64_t flags) {
         unsigned level = 1;
         int r;
 
@@ -54,7 +54,7 @@ int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags) {
         if (!f)
                 f = stdout;
 
-        if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) {
+        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,
                         m->header->type == SD_BUS_MESSAGE_METHOD_ERROR ? ansi_highlight_red() :
@@ -118,11 +118,11 @@ int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags) {
                 bus_creds_dump(&m->creds, f, true);
         }
 
-        r = sd_bus_message_rewind(m, !(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY));
+        r = sd_bus_message_rewind(m, !(flags & SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY));
         if (r < 0)
                 return log_error_errno(r, "Failed to rewind: %m");
 
-        if (!(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY)) {
+        if (!(flags & SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY)) {
                 _cleanup_free_ char *prefix = NULL;
 
                 prefix = indent(0, flags);
@@ -259,7 +259,7 @@ int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags) {
                 }
         }
 
-        if (!(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY)) {
+        if (!(flags & SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY)) {
                 _cleanup_free_ char *prefix = NULL;
 
                 prefix = indent(0, flags);
index a1b67c6b14f8692dc4b23d5226dc06ffb85c485b..f138791c04b3ef7f71c41f5b31dbc6fc8373f4bc 100644 (file)
@@ -6,13 +6,6 @@
 
 #include "sd-bus.h"
 
-enum {
-        BUS_MESSAGE_DUMP_WITH_HEADER  = 1 << 0,
-        BUS_MESSAGE_DUMP_SUBTREE_ONLY = 1 << 1,
-};
-
-int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags);
-
 int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse);
 
 int bus_pcap_header(size_t snaplen, FILE *f);
index beab80687d6f1a6e370f3ee2009d6d3924cbee34..e8934489b581f1b07a35609cbedeb35fb628aea6 100644 (file)
@@ -160,7 +160,7 @@ int introspect_write_interface(struct introspect *i, const sd_bus_vtable *v) {
                 case _SD_BUS_VTABLE_SIGNAL:
                         fprintf(i->f, "  <signal name=\"%s\">\n", v->x.signal.member);
                         if (bus_vtable_has_names(vtable))
-                                names = strempty(v->x.method.names);
+                                names = strempty(v->x.signal.names);
                         introspect_write_arguments(i, strempty(v->x.signal.signature), &names, NULL);
                         introspect_write_flags(i, v->type, v->flags);
                         fputs("  </signal>\n", i->f);
index b53d4dd854f9751c58caea9c01bc8baa39c6bb64..7ad03680f48d80d44adcf720412bbfa0e0fde887 100644 (file)
@@ -4207,3 +4207,27 @@ _public_ int sd_bus_get_close_on_exit(sd_bus *bus) {
 
         return bus->close_on_exit;
 }
+
+_public_ int sd_bus_enqueue_for_read(sd_bus *bus, sd_bus_message *m) {
+        int r;
+
+        assert_return(bus, -EINVAL);
+        assert_return(bus = bus_resolve(bus), -ENOPKG);
+        assert_return(m, -EINVAL);
+        assert_return(m->sealed, -EINVAL);
+        assert_return(!bus_pid_changed(bus), -ECHILD);
+
+        if (!BUS_IS_OPEN(bus->state))
+                return -ENOTCONN;
+
+        /* Re-enqueue a message for reading. This is primarily useful for PolicyKit-style authentication,
+         * where we accept a message, then determine we need to interactively authenticate the user, and then
+         * we want to process the message again. */
+
+        r = bus_rqueue_make_room(bus);
+        if (r < 0)
+                return r;
+
+        bus->rqueue[bus->rqueue_size++] = bus_message_ref_queued(m, bus);
+        return 0;
+}
index d9716ae74514f80fd83c82b41bb19c50b7093f5e..05127f0e0ce4930fbdd0c83d6a4421698bb145bd 100644 (file)
@@ -145,7 +145,7 @@ static int server(sd_bus *bus) {
                          strna(sd_bus_message_get_member(m)),
                          pid,
                          strna(label));
-                /* bus_message_dump(m); */
+                /* sd_bus_message_dump(m); */
                 /* sd_bus_message_rewind(m, true); */
 
                 if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "LowerCase")) {
index 1a9a35d56b469a27427ca22dc1722cd831d2fb47..d248bd4da133ab599b8edda02d3abe8cb1bbb9ed 100644 (file)
@@ -175,7 +175,7 @@ static int test_marshal(void) {
         }
 #endif
 
-        assert_se(bus_message_dump(m, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0);
+        assert_se(sd_bus_message_dump(m, NULL, SD_BUS_MESSAGE_DUMP_WITH_HEADER) >= 0);
 
         assert_se(bus_message_get_blob(m, &blob, &sz) >= 0);
 
@@ -196,7 +196,7 @@ static int test_marshal(void) {
         assert_se(bus_message_from_malloc(bus, blob, sz, NULL, 0, NULL, &n) >= 0);
         blob = NULL;
 
-        assert_se(bus_message_dump(n, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0);
+        assert_se(sd_bus_message_dump(n, NULL, SD_BUS_MESSAGE_DUMP_WITH_HEADER) >= 0);
 
         m = sd_bus_message_unref(m);
 
@@ -205,7 +205,7 @@ static int test_marshal(void) {
         assert_se(sd_bus_message_append(m, "as", 0) >= 0);
 
         assert_se(sd_bus_message_seal(m, 4712, 0) >= 0);
-        assert_se(bus_message_dump(m, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0);
+        assert_se(sd_bus_message_dump(m, NULL, SD_BUS_MESSAGE_DUMP_WITH_HEADER) >= 0);
 
         return EXIT_SUCCESS;
 }
index d7d51ba9504f4b2bc9196d8c8851d0de37503b51..107eea390e0bcccab0fd61bd454d6d988c24f920 100644 (file)
@@ -188,10 +188,10 @@ int main(int argc, char *argv[]) {
         r = sd_bus_message_seal(m, 4711, 0);
         assert_se(r >= 0);
 
-        bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+        sd_bus_message_dump(m, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
 
         ms = open_memstream_unlocked(&first, &first_size);
-        bus_message_dump(m, ms, 0);
+        sd_bus_message_dump(m, ms, 0);
         fflush(ms);
         assert_se(!ferror(ms));
 
@@ -244,11 +244,11 @@ int main(int argc, char *argv[]) {
         r = bus_message_from_malloc(bus, buffer, sz, NULL, 0, NULL, &m);
         assert_se(r >= 0);
 
-        bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+        sd_bus_message_dump(m, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
 
         fclose(ms);
         ms = open_memstream_unlocked(&second, &second_size);
-        bus_message_dump(m, ms, 0);
+        sd_bus_message_dump(m, ms, 0);
         fflush(ms);
         assert_se(!ferror(ms));
         assert_se(first_size == second_size);
@@ -354,7 +354,7 @@ int main(int argc, char *argv[]) {
 
         fclose(ms);
         ms = open_memstream_unlocked(&third, &third_size);
-        bus_message_dump(copy, ms, 0);
+        sd_bus_message_dump(copy, ms, 0);
         fflush(ms);
         assert_se(!ferror(ms));
 
index 3c5bb88f4e1d2dbceb1af7eb071f2c2e9a221d29..41cf8c167006793b462ba93ace85fad500ff1e44 100644 (file)
@@ -399,7 +399,7 @@ static int client(struct context *c) {
         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "");
         assert_se(r >= 0);
 
-        bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+        sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
 
         sd_bus_message_unref(reply);
         reply = NULL;
@@ -417,7 +417,7 @@ static int client(struct context *c) {
         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, "");
         assert_se(r >= 0);
 
-        bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+        sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
 
         sd_bus_message_unref(reply);
         reply = NULL;
@@ -429,7 +429,7 @@ static int client(struct context *c) {
         assert_se(r > 0);
 
         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged"));
-        bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+        sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
 
         sd_bus_message_unref(reply);
         reply = NULL;
@@ -441,7 +441,7 @@ static int client(struct context *c) {
         assert_se(r > 0);
 
         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged"));
-        bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+        sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
 
         sd_bus_message_unref(reply);
         reply = NULL;
@@ -453,7 +453,7 @@ static int client(struct context *c) {
         assert_se(r > 0);
 
         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"));
-        bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+        sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
 
         sd_bus_message_unref(reply);
         reply = NULL;
@@ -465,7 +465,7 @@ static int client(struct context *c) {
         assert_se(r > 0);
 
         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"));
-        bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+        sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
 
         sd_bus_message_unref(reply);
         reply = NULL;
@@ -477,7 +477,7 @@ static int client(struct context *c) {
         assert_se(r > 0);
 
         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"));
-        bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+        sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
 
         sd_bus_message_unref(reply);
         reply = NULL;
@@ -489,7 +489,7 @@ static int client(struct context *c) {
         assert_se(r > 0);
 
         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"));
-        bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER);
+        sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
 
         sd_bus_message_unref(reply);
         reply = NULL;
index e38bcdcc76fd827d710e1d0c7fa45949c8da87f5..82eb35e5b16db960c0b3c38840ced85a5c65bcb1 100644 (file)
@@ -108,7 +108,7 @@ fail:
 static int client(struct context *c) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
         _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
-        sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         int r;
 
         assert_se(sd_bus_new(&bus) >= 0);
index 2723866e08472c837466dd8da108318e34247517..7ded9386b75023f80e77277c35e0f0b8e5cb5ad9 100644 (file)
@@ -41,9 +41,9 @@ static const sd_bus_vtable vtable[] = {
 static void* thread_server(void *p) {
         _cleanup_free_ char *suffixed = NULL, *suffixed2 = NULL, *d = NULL;
         _cleanup_close_ int fd = -1;
-        union sockaddr_union u = {};
+        union sockaddr_union u;
         const char *path = p;
-        int salen;
+        int r;
 
         log_debug("Initializing server");
 
@@ -66,13 +66,15 @@ static void* thread_server(void *p) {
         assert_se(symlink(basename(suffixed), suffixed2) >= 0);
         (void) usleep(100 * USEC_PER_MSEC);
 
-        salen = sockaddr_un_set_path(&u.un, path);
-        assert_se(salen >= 0);
+        socklen_t sa_len;
+        r = sockaddr_un_set_path(&u.un, path);
+        assert_se(r >= 0);
+        sa_len = r;
 
         fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
         assert_se(fd >= 0);
 
-        assert_se(bind(fd, &u.sa, salen) >= 0);
+        assert_se(bind(fd, &u.sa, sa_len) >= 0);
         usleep(100 * USEC_PER_MSEC);
 
         assert_se(listen(fd, SOMAXCONN) >= 0);
index ff71194331b08fa43497da23db9300ff64f9bed7..4cd71cb2d3a022e6534fce90e6f0a89b589d5dfa 100644 (file)
@@ -443,7 +443,7 @@ _public_ int sd_pid_notify_with_fds(
                 const int *fds,
                 unsigned n_fds) {
 
-        union sockaddr_union sockaddr = {};
+        union sockaddr_union sockaddr;
         struct iovec iovec;
         struct msghdr msghdr = {
                 .msg_iov = &iovec,
@@ -454,7 +454,7 @@ _public_ int sd_pid_notify_with_fds(
         struct cmsghdr *cmsg = NULL;
         const char *e;
         bool send_ucred;
-        int r, salen;
+        int r;
 
         if (!state) {
                 r = -EINVAL;
@@ -470,11 +470,10 @@ _public_ int sd_pid_notify_with_fds(
         if (!e)
                 return 0;
 
-        salen = sockaddr_un_set_path(&sockaddr.un, e);
-        if (salen < 0) {
-                r = salen;
+        r = sockaddr_un_set_path(&sockaddr.un, e);
+        if (r < 0)
                 goto finish;
-        }
+        msghdr.msg_namelen = r;
 
         fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
         if (fd < 0) {
@@ -485,7 +484,6 @@ _public_ int sd_pid_notify_with_fds(
         (void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
 
         iovec = IOVEC_MAKE_STRING(state);
-        msghdr.msg_namelen = salen;
 
         send_ucred =
                 (pid != 0 && pid != getpid_cached()) ||
index c83575c7c876c84234c1ba3cff97e29297031e7f..d790e8fd0b197208f63b741696e5995839b89886 100644 (file)
@@ -446,7 +446,7 @@ static int insert_data(struct trie *trie, char **match_list, char *line, const c
 
         value = strchr(line, '=');
         if (!value)
-                return log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
+                return log_syntax(NULL, LOG_WARNING, filename, line_number, SYNTHETIC_ERRNO(EINVAL),
                                   "Key-value pair expected but got \"%s\", ignoring", line);
 
         value[0] = '\0';
@@ -457,7 +457,7 @@ static int insert_data(struct trie *trie, char **match_list, char *line, const c
                 line++;
 
         if (isempty(line + 1) || isempty(value))
-                return log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
+                return log_syntax(NULL, LOG_WARNING, filename, line_number, SYNTHETIC_ERRNO(EINVAL),
                                   "Empty %s in \"%s=%s\", ignoring",
                                   isempty(line + 1) ? "key" : "value",
                                   line, value);
@@ -477,7 +477,6 @@ static int import_file(struct trie *trie, const char *filename, uint16_t file_pr
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_strv_free_ char **match_list = NULL;
         uint32_t line_number = 0;
-        char *match = NULL;
         int r = 0, err;
 
         f = fopen(filename, "re");
@@ -518,20 +517,15 @@ static int import_file(struct trie *trie, const char *filename, uint16_t file_pr
                                 break;
 
                         if (line[0] == ' ') {
-                                log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
-                                           "Match expected but got indented property \"%s\", ignoring line", line);
-                                r = -EINVAL;
+                                r = log_syntax(NULL, LOG_WARNING, filename, line_number, SYNTHETIC_ERRNO(EINVAL),
+                                               "Match expected but got indented property \"%s\", ignoring line", line);
                                 break;
                         }
 
                         /* start of record, first match */
                         state = HW_MATCH;
 
-                        match = strdup(line);
-                        if (!match)
-                                return -ENOMEM;
-
-                        err = strv_consume(&match_list, match);
+                        err = strv_extend(&match_list, line);
                         if (err < 0)
                                 return err;
 
@@ -539,21 +533,16 @@ static int import_file(struct trie *trie, const char *filename, uint16_t file_pr
 
                 case HW_MATCH:
                         if (len == 0) {
-                                log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
-                                           "Property expected, ignoring record with no properties");
-                                r = -EINVAL;
+                                r = log_syntax(NULL, LOG_WARNING, filename, line_number, SYNTHETIC_ERRNO(EINVAL),
+                                               "Property expected, ignoring record with no properties");
                                 state = HW_NONE;
-                                strv_clear(match_list);
+                                match_list = strv_free(match_list);
                                 break;
                         }
 
                         if (line[0] != ' ') {
                                 /* another match */
-                                match = strdup(line);
-                                if (!match)
-                                        return -ENOMEM;
-
-                                err = strv_consume(&match_list, match);
+                                err = strv_extend(&match_list, line);
                                 if (err < 0)
                                         return err;
 
@@ -571,16 +560,15 @@ static int import_file(struct trie *trie, const char *filename, uint16_t file_pr
                         if (len == 0) {
                                 /* end of record */
                                 state = HW_NONE;
-                                strv_clear(match_list);
+                                match_list = strv_free(match_list);
                                 break;
                         }
 
                         if (line[0] != ' ') {
-                                log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
-                                           "Property or empty line expected, got \"%s\", ignoring record", line);
-                                r = -EINVAL;
+                                r = log_syntax(NULL, LOG_WARNING, filename, line_number, SYNTHETIC_ERRNO(EINVAL),
+                                               "Property or empty line expected, got \"%s\", ignoring record", line);
                                 state = HW_NONE;
-                                strv_clear(match_list);
+                                match_list = strv_free(match_list);
                                 break;
                         }
 
@@ -592,7 +580,7 @@ static int import_file(struct trie *trie, const char *filename, uint16_t file_pr
         }
 
         if (state == HW_MATCH)
-                log_syntax(NULL, LOG_WARNING, filename, line_number, EINVAL,
+                log_syntax(NULL, LOG_WARNING, filename, line_number, 0,
                            "Property expected, ignoring record with no properties");
 
         return r;
index 985872b82da48300a4bf9e430a568ce83afad457..335f22b9208da3b797b8c9e2b99435f872184b3c 100644 (file)
@@ -190,4 +190,17 @@ int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) {
         return memcmp(a, b, 16);
 }
 
+sd_id128_t id128_make_v4_uuid(sd_id128_t id) {
+        /* Stolen from generate_random_uuid() of drivers/char/random.c
+         * in the kernel sources */
+
+        /* Set UUID version to 4 --- truly random generation */
+        id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
+
+        /* Set the UUID variant to DCE */
+        id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
+
+        return id;
+}
+
 DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func);
index fe0149a8aa1b370d29489eca53e4bc370608f6b0..1901bf119fffb565b8606fc84cd15aa73102bd6d 100644 (file)
@@ -30,3 +30,5 @@ int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync);
 void id128_hash_func(const sd_id128_t *p, struct siphash *state);
 int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) _pure_;
 extern const struct hash_ops id128_hash_ops;
+
+sd_id128_t id128_make_v4_uuid(sd_id128_t id);
index b331a6b432ffe84facd92c8599d580e03a6ef07f..9b38ef0c563206f79b00dc397e9c1c55926d8adb 100644 (file)
@@ -250,19 +250,6 @@ _public_ int sd_id128_get_invocation(sd_id128_t *ret) {
         return 0;
 }
 
-static sd_id128_t make_v4_uuid(sd_id128_t id) {
-        /* Stolen from generate_random_uuid() of drivers/char/random.c
-         * in the kernel sources */
-
-        /* Set UUID version to 4 --- truly random generation */
-        id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
-
-        /* Set the UUID variant to DCE */
-        id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
-
-        return id;
-}
-
 _public_ int sd_id128_randomize(sd_id128_t *ret) {
         sd_id128_t t;
         int r;
@@ -279,7 +266,7 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
          * only guarantee this for newly generated UUIDs, not for
          * pre-existing ones. */
 
-        *ret = make_v4_uuid(t);
+        *ret = id128_make_v4_uuid(t);
         return 0;
 }
 
@@ -306,7 +293,7 @@ static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret)
         /* We chop off the trailing 16 bytes */
         memcpy(&result, p, MIN(khash_get_size(h), sizeof(result)));
 
-        *ret = make_v4_uuid(result);
+        *ret = id128_make_v4_uuid(result);
         return 0;
 }
 
index 39e3c36ad2aba25246b0535c6f80b7af268b698b..a5871c73197f634844785960801abb8900f426f0 100644 (file)
@@ -343,6 +343,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 e35127a4cd2ed97526cc3d1b5898dc722afa62dd..102cec49a1cc7fc22eceea0d470b6ffd9ad7ea82 100644 (file)
@@ -316,6 +316,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[] = {
@@ -745,6 +746,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 },
@@ -780,6 +787,23 @@ 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_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_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 +815,33 @@ 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_FQ] = "fq",
         [NL_UNION_TCA_OPTION_DATA_FQ_CODEL] = "fq_codel",
+        [NL_UNION_TCA_OPTION_DATA_GRED] = "gred",
+        [NL_UNION_TCA_OPTION_DATA_HTB] = "htb",
+        [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_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_HTB] =         { .count = ELEMENTSOF(rtnl_tca_option_data_htb_types),
+                                                   .types = rtnl_tca_option_data_htb_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 +854,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 +904,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..edc4a580b8507bbfa927191a15a8771545f2cf29 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,13 @@ 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_FQ,
         NL_UNION_TCA_OPTION_DATA_FQ_CODEL,
+        NL_UNION_TCA_OPTION_DATA_GRED,
+        NL_UNION_TCA_OPTION_DATA_HTB,
+        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 9c6a5d29f6e0f5f67d15e06c3d6901784ed87ae7..7387cffaa3a06adff5a2d866e8f4aee08194a983 100644 (file)
@@ -203,6 +203,29 @@ int rtnl_resolve_link_alternative_name(sd_netlink **rtnl, const char *name) {
         return ret;
 }
 
+int rtnl_get_link_iftype(sd_netlink **rtnl, int ifindex, unsigned short *ret) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
+        int r;
+
+        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 == -EINVAL)
+                return -ENODEV; /* The device does not exist */
+        if (r < 0)
+                return r;
+
+        return sd_rtnl_message_link_get_type(reply, ret);
+}
+
 int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret) {
         struct nlmsgerr *err;
         int r;
index 55bc12712a0c47eaf5382b098a55bc46c8db70d4..8ed94063e3d5a775c6240eb3730563c529c02893 100644 (file)
@@ -47,11 +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_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_resolve_link_alternative_name(sd_netlink **rtnl, const char *name);
+int rtnl_get_link_iftype(sd_netlink **rtnl, int ifindex, unsigned short *ret);
 
 int rtnl_log_parse_error(int r);
 int rtnl_log_create_error(int r);
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 08ed9426389c7fbbd8df1a9b8261b0fb0ab338de..0addabe10a2496338e2f9a4182604cb96b0b8a3a 100644 (file)
@@ -26,6 +26,7 @@ bool network_is_online(void) {
 }
 
 static const char* const link_operstate_table[_LINK_OPERSTATE_MAX] = {
+        [LINK_OPERSTATE_MISSING]          = "missing",
         [LINK_OPERSTATE_OFF]              = "off",
         [LINK_OPERSTATE_NO_CARRIER]       = "no-carrier",
         [LINK_OPERSTATE_DORMANT]          = "dormant",
@@ -56,3 +57,49 @@ static const char* const link_address_state_table[_LINK_ADDRESS_STATE_MAX] = {
 };
 
 DEFINE_STRING_TABLE_LOOKUP(link_address_state, LinkAddressState);
+
+int parse_operational_state_range(const char *str, LinkOperationalStateRange *out) {
+        LinkOperationalStateRange range = { _LINK_OPERSTATE_INVALID, _LINK_OPERSTATE_INVALID };
+        _cleanup_free_ const char *min = NULL;
+        const char *p;
+
+        assert(str);
+        assert(out);
+
+        p = strchr(str, ':');
+        if (p) {
+                min = strndup(str, p - str);
+
+                if (!isempty(p + 1)) {
+                        range.max = link_operstate_from_string(p + 1);
+                        if (range.max < 0)
+                                return -EINVAL;
+                }
+        } else
+                min = strdup(str);
+
+        if (!min)
+                return -ENOMEM;
+
+        if (!isempty(min)) {
+                range.min = link_operstate_from_string(min);
+                if (range.min < 0)
+                        return -EINVAL;
+        }
+
+        /* Fail on empty strings. */
+        if (range.min == _LINK_OPERSTATE_INVALID && range.max == _LINK_OPERSTATE_INVALID)
+                return -EINVAL;
+
+        if (range.min == _LINK_OPERSTATE_INVALID)
+                range.min = LINK_OPERSTATE_MISSING;
+        if (range.max == _LINK_OPERSTATE_INVALID)
+                range.max = LINK_OPERSTATE_ROUTABLE;
+
+        if (range.min > range.max)
+                return -EINVAL;
+
+        *out = range;
+
+        return 0;
+}
index a19435393df6c3a61d2f716698bc1a4ca6ed0dac..425d192f6402a6dc6bdea3d1f98be73427718a71 100644 (file)
@@ -8,6 +8,7 @@
 bool network_is_online(void);
 
 typedef enum LinkOperationalState {
+        LINK_OPERSTATE_MISSING,
         LINK_OPERSTATE_OFF,
         LINK_OPERSTATE_NO_CARRIER,
         LINK_OPERSTATE_DORMANT,
@@ -47,3 +48,13 @@ LinkCarrierState link_carrier_state_from_string(const char *s) _pure_;
 
 const char* link_address_state_to_string(LinkAddressState s) _const_;
 LinkAddressState link_address_state_from_string(const char *s) _pure_;
+
+typedef struct LinkOperationalStateRange {
+        LinkOperationalState min;
+        LinkOperationalState max;
+} LinkOperationalStateRange;
+
+#define LINK_OPERSTATE_RANGE_DEFAULT (LinkOperationalStateRange) { LINK_OPERSTATE_DEGRADED, \
+                                                                   LINK_OPERSTATE_ROUTABLE }
+
+int parse_operational_state_range(const char *str, LinkOperationalStateRange *out);
index 519dd0d188cfea6abd2f3efb26578989d7c291c0..30669a9359e58104898dcc1e9c460db27fee9562 100644 (file)
@@ -5,7 +5,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include "bus-util.h"
+#include "bus-polkit.h"
 #include "env-file-label.h"
 #include "env-file.h"
 #include "env-util.h"
index baf0bd102ba99d58fdf25749affb35e1f69fad87..09f16d25f4c20e5fa928dcce2dabaf34e3d0982b 100644 (file)
@@ -15,7 +15,7 @@
 #include "alloc-util.h"
 #include "bus-error.h"
 #include "bus-message.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
 #include "def.h"
 #include "keymap-util.h"
 #include "locale-util.h"
@@ -33,7 +33,7 @@ static int locale_update_system_manager(Context *c, sd_bus *bus) {
         _cleanup_free_ char **l_unset = NULL;
         _cleanup_strv_free_ char **l_set = NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
-        sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         size_t c_set, c_unset;
         LocaleVariable p;
         int r;
index 2a9ddb93aa73f998cd89df76eb1f7e1149a5631c..2bbd18363e69a817dd0c769c489b53fbacbb1f08 100644 (file)
@@ -32,12 +32,9 @@ SUBSYSTEM=="pci", ENV{ID_PCI_CLASS_FROM_DATABASE}=="Display controller", \
 SUBSYSTEM=="drm", KERNEL=="card[0-9]*", TAG+="seat", TAG+="master-of-seat"
 SUBSYSTEM=="usb", ATTR{bDeviceClass}=="09", TAG+="seat"
 
-# 'Plugable UD-160' USB hub, sound, network, graphics adapter
+# 'Plugable' USB hub, sound, network, graphics adapter
 SUBSYSTEM=="usb", ATTR{idVendor}=="2230", ATTR{idProduct}=="000[13]", ENV{ID_AUTOSEAT}="1"
 
-# 'Plugable UD-PRO8' USB hub, sound, network, graphics adapter
-SUBSYSTEM=="usb", ATTR{idVendor}=="1a40", ATTR{idProduct}=="0201", ENV{ID_AUTOSEAT}="1"
-
 # qemu (version 2.4+) has a PCI-PCI bridge (-device pci-bridge-seat) to group
 # devices belonging to one seat. See:
 #     http://git.qemu.org/?p=qemu.git;a=blob;f=docs/multiseat.txt
index bff63541771fcbfa2feacca5c45a1354d35d3912..2e39f557632fffb8e020478704a23b5b6bc6936f 100644 (file)
@@ -576,6 +576,7 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li
                         show_journal_by_unit(
                                         stdout,
                                         i.scope,
+                                        NULL,
                                         arg_output,
                                         0,
                                         i.timestamp.monotonic,
@@ -660,6 +661,7 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line)
                 show_journal_by_unit(
                                 stdout,
                                 i.slice,
+                                NULL,
                                 arg_output,
                                 0,
                                 i.timestamp.monotonic,
@@ -981,7 +983,6 @@ 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;
-        char *short_argv[3];
         int r, i;
 
         assert(bus);
@@ -990,12 +991,20 @@ static int activate(int argc, char *argv[], void *userdata) {
         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
         if (argc < 2) {
-                short_argv[0] = argv[0];
-                short_argv[1] = (char*) "";
-                short_argv[2] = NULL;
+                r = sd_bus_call_method(
+                                bus,
+                                "org.freedesktop.login1",
+                                "/org/freedesktop/login1/session/auto",
+                                "org.freedesktop.login1.Session",
+                                streq(argv[0], "lock-session")      ? "Lock" :
+                                streq(argv[0], "unlock-session")    ? "Unlock" :
+                                streq(argv[0], "terminate-session") ? "Terminate" :
+                                                                      "Activate",
+                                &error, NULL, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
 
-                argv = short_argv;
-                argc = 2;
+                return 0;
         }
 
         for (i = 1; i < argc; i++) {
index e84b38096a1c50cc432497f29380b5ea8c43873b..22a42b077c0d6ff7a12f531382e1fa0c0f1b9401 100644 (file)
@@ -27,6 +27,7 @@
 #include "terminal-util.h"
 #include "udev-util.h"
 #include "user-util.h"
+#include "userdb.h"
 
 void manager_reset_config(Manager *m) {
         assert(m);
@@ -137,21 +138,18 @@ int manager_add_session(Manager *m, const char *id, Session **ret_session) {
 
 int manager_add_user(
                 Manager *m,
-                uid_t uid,
-                gid_t gid,
-                const char *name,
-                const char *home,
+                UserRecord *ur,
                 User **ret_user) {
 
         User *u;
         int r;
 
         assert(m);
-        assert(name);
+        assert(ur);
 
-        u = hashmap_get(m->users, UID_TO_PTR(uid));
+        u = hashmap_get(m->users, UID_TO_PTR(ur->uid));
         if (!u) {
-                r = user_new(&u, m, uid, gid, name, home);
+                r = user_new(&u, m, ur);
                 if (r < 0)
                         return r;
         }
@@ -167,32 +165,35 @@ int manager_add_user_by_name(
                 const char *name,
                 User **ret_user) {
 
-        const char *home = NULL;
-        uid_t uid;
-        gid_t gid;
+        _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
         int r;
 
         assert(m);
         assert(name);
 
-        r = get_user_creds(&name, &uid, &gid, &home, NULL, 0);
+        r = userdb_by_name(name, 0, &ur);
         if (r < 0)
                 return r;
 
-        return manager_add_user(m, uid, gid, name, home, ret_user);
+        return manager_add_user(m, ur, ret_user);
 }
 
-int manager_add_user_by_uid(Manager *m, uid_t uid, User **ret_user) {
-        struct passwd *p;
+int manager_add_user_by_uid(
+                Manager *m,
+                uid_t uid,
+                User **ret_user) {
+
+        _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+        int r;
 
         assert(m);
+        assert(uid_is_valid(uid));
 
-        errno = 0;
-        p = getpwuid(uid);
-        if (!p)
-                return errno_or_else(ENOENT);
+        r = userdb_by_uid(uid, 0, &ur);
+        if (r < 0)
+                return r;
 
-        return manager_add_user(m, uid, p->pw_gid, p->pw_name, p->pw_dir, ret_user);
+        return manager_add_user(m, ur, ret_user);
 }
 
 int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **ret) {
index 69b59948786f62a18a8facecea37bc722f3769f3..52a7ea3c77e9ff8f1418045ecab709613be3d188 100644 (file)
 #include "bootspec.h"
 #include "bus-common-errors.h"
 #include "bus-error.h"
+#include "bus-polkit.h"
 #include "bus-unit-util.h"
 #include "bus-util.h"
 #include "cgroup-util.h"
 #include "device-util.h"
 #include "dirent-util.h"
-#include "efivars.h"
 #include "efi-loader.h"
+#include "efivars.h"
 #include "env-util.h"
 #include "escape.h"
 #include "fd-util.h"
@@ -536,8 +537,8 @@ static int method_list_sessions(sd_bus_message *message, void *userdata, sd_bus_
 
                 r = sd_bus_message_append(reply, "(susso)",
                                           session->id,
-                                          (uint32_t) session->user->uid,
-                                          session->user->name,
+                                          (uint32_t) session->user->user_record->uid,
+                                          session->user->user_record->user_name,
                                           session->seat ? session->seat->id : "",
                                           p);
                 if (r < 0)
@@ -577,8 +578,8 @@ static int method_list_users(sd_bus_message *message, void *userdata, sd_bus_err
                         return -ENOMEM;
 
                 r = sd_bus_message_append(reply, "(uso)",
-                                          (uint32_t) user->uid,
-                                          user->name,
+                                          (uint32_t) user->user_record->uid,
+                                          user->user_record->user_name,
                                           p);
                 if (r < 0)
                         return r;
@@ -1015,6 +1016,8 @@ static int method_activate_session(sd_bus_message *message, void *userdata, sd_b
         if (r < 0)
                 return r;
 
+        /* PolicyKit is done by bus_session_method_activate() */
+
         return bus_session_method_activate(message, session, error);
 }
 
@@ -1046,6 +1049,20 @@ static int method_activate_session_on_seat(sd_bus_message *message, void *userda
                 return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT,
                                          "Session %s not on seat %s", session_name, seat_name);
 
+        r = bus_verify_polkit_async(
+                        message,
+                        CAP_SYS_ADMIN,
+                        "org.freedesktop.login1.chvt",
+                        NULL,
+                        false,
+                        UID_INVALID,
+                        &m->polkit_registry,
+                        error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* Will call us back */
+
         r = session_activate(session);
         if (r < 0)
                 return r;
@@ -1488,7 +1505,7 @@ static int have_multiple_sessions(
          * count, and non-login sessions do not count either. */
         HASHMAP_FOREACH(session, m->sessions, i)
                 if (session->class == SESSION_USER &&
-                    session->user->uid != uid)
+                    session->user->user_record->uid != uid)
                         return true;
 
         return false;
index 34ac0350f2dd93b69d455b5740ae71396e82dc9d..0a5df937cc1a630523383bbf85ec98462647587f 100644 (file)
@@ -5,6 +5,7 @@
 #include "alloc-util.h"
 #include "bus-common-errors.h"
 #include "bus-label.h"
+#include "bus-polkit.h"
 #include "bus-util.h"
 #include "logind-dbus.h"
 #include "logind-seat-dbus.h"
@@ -177,6 +178,20 @@ static int method_activate_session(sd_bus_message *message, void *userdata, sd_b
         if (session->seat != s)
                 return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, "Session %s not on seat %s", name, s->id);
 
+        r = bus_verify_polkit_async(
+                        message,
+                        CAP_SYS_ADMIN,
+                        "org.freedesktop.login1.chvt",
+                        NULL,
+                        false,
+                        UID_INVALID,
+                        &s->manager->polkit_registry,
+                        error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* Will call us back */
+
         r = session_activate(session);
         if (r < 0)
                 return r;
@@ -197,7 +212,21 @@ static int method_switch_to(sd_bus_message *message, void *userdata, sd_bus_erro
                 return r;
 
         if (to <= 0)
-                return -EINVAL;
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid virtual terminal");
+
+        r = bus_verify_polkit_async(
+                        message,
+                        CAP_SYS_ADMIN,
+                        "org.freedesktop.login1.chvt",
+                        NULL,
+                        false,
+                        UID_INVALID,
+                        &s->manager->polkit_registry,
+                        error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* Will call us back */
 
         r = seat_switch_to(s, to);
         if (r < 0)
@@ -213,6 +242,20 @@ static int method_switch_to_next(sd_bus_message *message, void *userdata, sd_bus
         assert(message);
         assert(s);
 
+        r = bus_verify_polkit_async(
+                        message,
+                        CAP_SYS_ADMIN,
+                        "org.freedesktop.login1.chvt",
+                        NULL,
+                        false,
+                        UID_INVALID,
+                        &s->manager->polkit_registry,
+                        error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* Will call us back */
+
         r = seat_switch_to_next(s);
         if (r < 0)
                 return r;
@@ -227,6 +270,20 @@ static int method_switch_to_previous(sd_bus_message *message, void *userdata, sd
         assert(message);
         assert(s);
 
+        r = bus_verify_polkit_async(
+                        message,
+                        CAP_SYS_ADMIN,
+                        "org.freedesktop.login1.chvt",
+                        NULL,
+                        false,
+                        UID_INVALID,
+                        &s->manager->polkit_registry,
+                        error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* Will call us back */
+
         r = seat_switch_to_previous(s);
         if (r < 0)
                 return r;
index 026bcf4d8eb9a065909f525d329954f51cc10547..394f89dd7e0158d1300b73b5ac11e94f428c7af7 100644 (file)
@@ -119,7 +119,7 @@ int seat_save(Seat *s) {
                         "ACTIVE=%s\n"
                         "ACTIVE_UID="UID_FMT"\n",
                         s->active->id,
-                        s->active->user->uid);
+                        s->active->user->user_record->uid);
         }
 
         if (s->sessions) {
@@ -137,7 +137,7 @@ int seat_save(Seat *s) {
                 LIST_FOREACH(sessions_by_seat, i, s->sessions)
                         fprintf(f,
                                 UID_FMT"%c",
-                                i->user->uid,
+                                i->user->user_record->uid,
                                 i->sessions_by_seat_next ? ' ' : '\n');
         }
 
@@ -216,8 +216,8 @@ int seat_apply_acls(Seat *s, Session *old_active) {
 
         r = devnode_acl_all(s->id,
                             false,
-                            !!old_active, old_active ? old_active->user->uid : 0,
-                            !!s->active, s->active ? s->active->user->uid : 0);
+                            !!old_active, old_active ? old_active->user->user_record->uid : 0,
+                            !!s->active, s->active ? s->active->user->user_record->uid : 0);
 
         if (r < 0)
                 return log_error_errno(r, "Failed to apply ACLs: %m");
index 8f13edafb00fd991ba447b208fa4c412a17b698a..80ec89ba0a0084eeef3cb08f5e881d864fea7aa4 100644 (file)
@@ -5,6 +5,7 @@
 #include "alloc-util.h"
 #include "bus-common-errors.h"
 #include "bus-label.h"
+#include "bus-polkit.h"
 #include "bus-util.h"
 #include "fd-util.h"
 #include "logind-brightness.h"
@@ -43,7 +44,7 @@ static int property_get_user(
         if (!p)
                 return -ENOMEM;
 
-        return sd_bus_message_append(reply, "(uo)", (uint32_t) s->user->uid, p);
+        return sd_bus_message_append(reply, "(uo)", (uint32_t) s->user->user_record->uid, p);
 }
 
 static int property_get_name(
@@ -61,7 +62,7 @@ static int property_get_name(
         assert(reply);
         assert(s);
 
-        return sd_bus_message_append(reply, "s", s->user->name);
+        return sd_bus_message_append(reply, "s", s->user->user_record->user_name);
 }
 
 static int property_get_seat(
@@ -168,7 +169,7 @@ int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus
                         "org.freedesktop.login1.manage",
                         NULL,
                         false,
-                        s->user->uid,
+                        s->user->user_record->uid,
                         &s->manager->polkit_registry,
                         error);
         if (r < 0)
@@ -190,6 +191,20 @@ int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_
         assert(message);
         assert(s);
 
+        r = bus_verify_polkit_async(
+                        message,
+                        CAP_SYS_ADMIN,
+                        "org.freedesktop.login1.chvt",
+                        NULL,
+                        false,
+                        UID_INVALID,
+                        &s->manager->polkit_registry,
+                        error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* Will call us back */
+
         r = session_activate(s);
         if (r < 0)
                 return r;
@@ -210,7 +225,7 @@ int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_erro
                         "org.freedesktop.login1.lock-sessions",
                         NULL,
                         false,
-                        s->user->uid,
+                        s->user->user_record->uid,
                         &s->manager->polkit_registry,
                         error);
         if (r < 0)
@@ -246,10 +261,14 @@ static int method_set_idle_hint(sd_bus_message *message, void *userdata, sd_bus_
         if (r < 0)
                 return r;
 
-        if (uid != 0 && uid != s->user->uid)
+        if (uid != 0 && uid != s->user->user_record->uid)
                 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set idle hint");
 
-        session_set_idle_hint(s, b);
+        r = session_set_idle_hint(s, b);
+        if (r == -ENOTTY)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Idle hint control is not supported on non-graphical sessions.");
+        if (r < 0)
+                return r;
 
         return sd_bus_reply_method_return(message, NULL);
 }
@@ -275,7 +294,7 @@ static int method_set_locked_hint(sd_bus_message *message, void *userdata, sd_bu
         if (r < 0)
                 return r;
 
-        if (uid != 0 && uid != s->user->uid)
+        if (uid != 0 && uid != s->user->user_record->uid)
                 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set locked hint");
 
         session_set_locked_hint(s, b);
@@ -314,7 +333,7 @@ int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro
                         "org.freedesktop.login1.manage",
                         NULL,
                         false,
-                        s->user->uid,
+                        s->user->user_record->uid,
                         &s->manager->polkit_registry,
                         error);
         if (r < 0)
@@ -350,7 +369,7 @@ static int method_take_control(sd_bus_message *message, void *userdata, sd_bus_e
         if (r < 0)
                 return r;
 
-        if (uid != 0 && (force || uid != s->user->uid))
+        if (uid != 0 && (force || uid != s->user->user_record->uid))
                 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may take control");
 
         r = session_set_controller(s, sd_bus_message_get_sender(message), force, true);
@@ -519,7 +538,7 @@ static int method_set_brightness(sd_bus_message *message, void *userdata, sd_bus
         if (r < 0)
                 return r;
 
-        if (uid != 0 && uid != s->user->uid)
+        if (uid != 0 && uid != s->user->user_record->uid)
                 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may change brightness.");
 
         r = sd_device_new_from_subsystem_sysname(&d, subsystem, name);
@@ -818,7 +837,7 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
                   "session_fd=%d seat=%s vtnr=%u",
                   s->id,
                   p,
-                  (uint32_t) s->user->uid,
+                  (uint32_t) s->user->user_record->uid,
                   s->user->runtime_path,
                   fifo_fd,
                   s->seat ? s->seat->id : "",
@@ -830,7 +849,7 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
                         p,
                         s->user->runtime_path,
                         fifo_fd,
-                        (uint32_t) s->user->uid,
+                        (uint32_t) s->user->user_record->uid,
                         s->seat ? s->seat->id : "",
                         (uint32_t) s->vtnr,
                         false);
index 1ef73570e58b52bf35d4ae968dd62e129dd736c2..48505122a9decf670e04c1ea6401cc97131b8e1d 100644 (file)
@@ -230,8 +230,8 @@ int session_save(Session *s) {
                 "IS_DISPLAY=%i\n"
                 "STATE=%s\n"
                 "REMOTE=%i\n",
-                s->user->uid,
-                s->user->name,
+                s->user->user_record->uid,
+                s->user->user_record->user_name,
                 session_is_active(s),
                 s->user->display == s,
                 session_state_to_string(session_get_state(s)),
@@ -641,7 +641,7 @@ static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_er
                 if (!scope)
                         return log_oom();
 
-                description = strjoina("Session ", s->id, " of user ", s->user->name);
+                description = strjoina("Session ", s->id, " of user ", s->user->user_record->user_name);
 
                 r = manager_start_scope(
                                 s->manager,
@@ -657,7 +657,7 @@ static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_er
                                           "systemd-user-sessions.service",
                                           s->user->runtime_dir_service,
                                           s->user->service),
-                                s->user->home,
+                                user_record_home_directory(s->user->user_record),
                                 properties,
                                 error,
                                 &s->scope_job);
@@ -698,9 +698,9 @@ int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error) {
         log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
                    "MESSAGE_ID=" SD_MESSAGE_SESSION_START_STR,
                    "SESSION_ID=%s", s->id,
-                   "USER_ID=%s", s->user->name,
+                   "USER_ID=%s", s->user->user_record->user_name,
                    "LEADER="PID_FMT, s->leader,
-                   LOG_MESSAGE("New session %s of user %s.", s->id, s->user->name));
+                   LOG_MESSAGE("New session %s of user %s.", s->id, s->user->user_record->user_name));
 
         if (!dual_timestamp_is_set(&s->timestamp))
                 dual_timestamp_get(&s->timestamp);
@@ -750,7 +750,10 @@ static int session_stop_scope(Session *s, bool force) {
         s->scope_job = mfree(s->scope_job);
 
         /* Optionally, let's kill everything that's left now. */
-        if (force || manager_shall_kill(s->manager, s->user->name)) {
+        if (force ||
+            (s->user->user_record->kill_processes != 0 &&
+             (s->user->user_record->kill_processes > 0 ||
+              manager_shall_kill(s->manager, s->user->user_record->user_name)))) {
 
                 r = manager_stop_unit(s->manager, s->scope, &error, &s->scope_job);
                 if (r < 0) {
@@ -766,7 +769,7 @@ static int session_stop_scope(Session *s, bool force) {
                  * Session stop is quite significant on its own, let's log it. */
                 log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
                            "SESSION_ID=%s", s->id,
-                           "USER_ID=%s", s->user->name,
+                           "USER_ID=%s", s->user->user_record->user_name,
                            "LEADER="PID_FMT, s->leader,
                            LOG_MESSAGE("Session %s logged out. Waiting for processes to exit.", s->id));
         }
@@ -824,7 +827,7 @@ int session_finalize(Session *s) {
                 log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
                            "MESSAGE_ID=" SD_MESSAGE_SESSION_STOP_STR,
                            "SESSION_ID=%s", s->id,
-                           "USER_ID=%s", s->user->name,
+                           "USER_ID=%s", s->user->user_record->user_name,
                            "LEADER="PID_FMT, s->leader,
                            LOG_MESSAGE("Removed session %s.", s->id));
 
@@ -932,63 +935,57 @@ static int get_process_ctty_atime(pid_t pid, usec_t *atime) {
 }
 
 int session_get_idle_hint(Session *s, dual_timestamp *t) {
-        usec_t atime = 0, n;
+        usec_t atime = 0;
         int r;
 
         assert(s);
 
-        /* Explicit idle hint is set */
-        if (s->idle_hint) {
+        /* Graphical sessions have an explicit idle hint */
+        if (SESSION_TYPE_IS_GRAPHICAL(s->type)) {
                 if (t)
                         *t = s->idle_hint_timestamp;
 
                 return s->idle_hint;
         }
 
-        /* Graphical sessions should really implement a real
-         * idle hint logic */
-        if (SESSION_TYPE_IS_GRAPHICAL(s->type))
-                goto dont_know;
-
-        /* For sessions with an explicitly configured tty, let's check
-         * its atime */
+        /* For sessions with an explicitly configured tty, let's check its atime */
         if (s->tty) {
                 r = get_tty_atime(s->tty, &atime);
                 if (r >= 0)
                         goto found_atime;
         }
 
-        /* For sessions with a leader but no explicitly configured
-         * tty, let's check the controlling tty of the leader */
+        /* For sessions with a leader but no explicitly configured tty, let's check the controlling tty of
+         * the leader */
         if (pid_is_valid(s->leader)) {
                 r = get_process_ctty_atime(s->leader, &atime);
                 if (r >= 0)
                         goto found_atime;
         }
 
-dont_know:
         if (t)
-                *t = s->idle_hint_timestamp;
+                *t = DUAL_TIMESTAMP_NULL;
 
-        return 0;
+        return false;
 
 found_atime:
         if (t)
                 dual_timestamp_from_realtime(t, atime);
 
-        n = now(CLOCK_REALTIME);
-
         if (s->manager->idle_action_usec <= 0)
-                return 0;
+                return false;
 
-        return atime + s->manager->idle_action_usec <= n;
+        return usec_add(atime, s->manager->idle_action_usec) <= now(CLOCK_REALTIME);
 }
 
-void session_set_idle_hint(Session *s, bool b) {
+int session_set_idle_hint(Session *s, bool b) {
         assert(s);
 
+        if (!SESSION_TYPE_IS_GRAPHICAL(s->type))
+                return -ENOTTY;
+
         if (s->idle_hint == b)
-                return;
+                return 0;
 
         s->idle_hint = b;
         dual_timestamp_get(&s->idle_hint_timestamp);
@@ -1000,6 +997,8 @@ void session_set_idle_hint(Session *s, bool b) {
 
         user_send_changed(s->user, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
         manager_send_changed(s->manager, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
+
+        return 1;
 }
 
 int session_get_locked_hint(Session *s) {
@@ -1193,7 +1192,7 @@ static int session_prepare_vt(Session *s) {
         if (vt < 0)
                 return vt;
 
-        r = fchown(vt, s->user->uid, -1);
+        r = fchown(vt, s->user->user_record->uid, -1);
         if (r < 0) {
                 r = log_error_errno(errno,
                                     "Cannot change owner of /dev/tty%u: %m",
index 28b01d2b9a63a688ee5af0612ced813266ee1c6e..c51392bef655ef533c8d5ed95313aeabab69d269 100644 (file)
@@ -132,7 +132,7 @@ void session_add_to_gc_queue(Session *s);
 int session_activate(Session *s);
 bool session_is_active(Session *s);
 int session_get_idle_hint(Session *s, dual_timestamp *t);
-void session_set_idle_hint(Session *s, bool b);
+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);
 int session_create_fifo(Session *s);
index 1dc222f90ef51c344ac31ab232e663a5c134902c..9bf68a610e409ac2f9cef55e673008c19864f0ac 100644 (file)
@@ -3,6 +3,7 @@
 #include <errno.h>
 
 #include "alloc-util.h"
+#include "bus-polkit.h"
 #include "bus-util.h"
 #include "format-util.h"
 #include "logind-dbus.h"
 #include "strv.h"
 #include "user-util.h"
 
+static int property_get_uid(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        User *u = userdata;
+
+        assert(bus);
+        assert(reply);
+        assert(u);
+
+        return sd_bus_message_append(reply, "u", (uint32_t) u->user_record->uid);
+}
+
+static int property_get_gid(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        User *u = userdata;
+
+        assert(bus);
+        assert(reply);
+        assert(u);
+
+        return sd_bus_message_append(reply, "u", (uint32_t) u->user_record->gid);
+}
+
+static int property_get_name(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        User *u = userdata;
+
+        assert(bus);
+        assert(reply);
+        assert(u);
+
+        return sd_bus_message_append(reply, "s", u->user_record->user_name);
+}
+
 static BUS_DEFINE_PROPERTY_GET2(property_get_state, "s", User, user_get_state, user_state_to_string);
 
 static int property_get_display(
@@ -152,7 +207,7 @@ int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_er
                         "org.freedesktop.login1.manage",
                         NULL,
                         false,
-                        u->uid,
+                        u->user_record->uid,
                         &u->manager->polkit_registry,
                         error);
         if (r < 0)
@@ -181,7 +236,7 @@ int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
                         "org.freedesktop.login1.manage",
                         NULL,
                         false,
-                        u->uid,
+                        u->user_record->uid,
                         &u->manager->polkit_registry,
                         error);
         if (r < 0)
@@ -206,9 +261,9 @@ int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
 const sd_bus_vtable user_vtable[] = {
         SD_BUS_VTABLE_START(0),
 
-        SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(User, uid), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(User, gid), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Name", "s", NULL, offsetof(User, name), SD_BUS_VTABLE_PROPERTY_CONST),
+        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),
@@ -276,7 +331,7 @@ char *user_bus_path(User *u) {
 
         assert(u);
 
-        if (asprintf(&s, "/org/freedesktop/login1/user/_"UID_FMT, u->uid) < 0)
+        if (asprintf(&s, "/org/freedesktop/login1/user/_"UID_FMT, u->user_record->uid) < 0)
                 return NULL;
 
         return s;
@@ -345,7 +400,7 @@ int user_send_signal(User *u, bool new_user) {
                         "/org/freedesktop/login1",
                         "org.freedesktop.login1.Manager",
                         new_user ? "UserNew" : "UserRemoved",
-                        "uo", (uint32_t) u->uid, p);
+                        "uo", (uint32_t) u->user_record->uid, p);
 }
 
 int user_send_changed(User *u, const char *properties, ...) {
index 1550431bea5c3bb2253b77e3403efc583730d773..9ceb33cde911b60d014c756e914b5ea33d03d4ad 100644 (file)
 
 int user_new(User **ret,
              Manager *m,
-             uid_t uid,
-             gid_t gid,
-             const char *name,
-             const char *home) {
+             UserRecord *ur) {
 
         _cleanup_(user_freep) User *u = NULL;
         char lu[DECIMAL_STR_MAX(uid_t) + 1];
@@ -48,7 +45,13 @@ int user_new(User **ret,
 
         assert(ret);
         assert(m);
-        assert(name);
+        assert(ur);
+
+        if (!ur->user_name)
+                return -EINVAL;
+
+        if (!uid_is_valid(ur->uid))
+                return -EINVAL;
 
         u = new(User, 1);
         if (!u)
@@ -56,28 +59,17 @@ int user_new(User **ret,
 
         *u = (User) {
                 .manager = m,
-                .uid = uid,
-                .gid = gid,
+                .user_record = user_record_ref(ur),
                 .last_session_timestamp = USEC_INFINITY,
         };
 
-        u->name = strdup(name);
-        if (!u->name)
-                return -ENOMEM;
-
-        u->home = strdup(home);
-        if (!u->home)
-                return -ENOMEM;
-
-        path_simplify(u->home, true);
-
-        if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0)
+        if (asprintf(&u->state_file, "/run/systemd/users/" UID_FMT, ur->uid) < 0)
                 return -ENOMEM;
 
-        if (asprintf(&u->runtime_path, "/run/user/"UID_FMT, uid) < 0)
+        if (asprintf(&u->runtime_path, "/run/user/" UID_FMT, ur->uid) < 0)
                 return -ENOMEM;
 
-        xsprintf(lu, UID_FMT, uid);
+        xsprintf(lu, UID_FMT, ur->uid);
         r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &u->slice);
         if (r < 0)
                 return r;
@@ -90,7 +82,7 @@ int user_new(User **ret,
         if (r < 0)
                 return r;
 
-        r = hashmap_put(m->users, UID_TO_PTR(uid), u);
+        r = hashmap_put(m->users, UID_TO_PTR(ur->uid), u);
         if (r < 0)
                 return r;
 
@@ -129,9 +121,9 @@ User *user_free(User *u) {
         if (u->slice)
                 hashmap_remove_value(u->manager->user_units, u->slice, u);
 
-        hashmap_remove_value(u->manager->users, UID_TO_PTR(u->uid), u);
+        hashmap_remove_value(u->manager->users, UID_TO_PTR(u->user_record->uid), u);
 
-        (void) sd_event_source_unref(u->timer_event_source);
+        sd_event_source_unref(u->timer_event_source);
 
         u->service_job = mfree(u->service_job);
 
@@ -140,8 +132,8 @@ User *user_free(User *u) {
         u->slice = mfree(u->slice);
         u->runtime_path = mfree(u->runtime_path);
         u->state_file = mfree(u->state_file);
-        u->name = mfree(u->name);
-        u->home = mfree(u->home);
+
+        user_record_unref(u->user_record);
 
         return mfree(u);
 }
@@ -169,7 +161,7 @@ static int user_save_internal(User *u) {
                 "NAME=%s\n"
                 "STATE=%s\n"         /* friendly user-facing state */
                 "STOPPING=%s\n",     /* low-level state */
-                u->name,
+                u->user_record->user_name,
                 user_state_to_string(user_get_state(u)),
                 yes_no(u->stopping));
 
@@ -363,6 +355,88 @@ static void user_start_service(User *u) {
                                "Failed to start user service '%s', ignoring: %s", u->service, bus_error_message(&error, r));
 }
 
+static int update_slice_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
+        _cleanup_(user_record_unrefp) UserRecord *ur = userdata;
+
+        assert(m);
+        assert(ur);
+
+        if (sd_bus_message_is_method_error(m, NULL)) {
+                log_warning_errno(sd_bus_message_get_errno(m),
+                                  "Failed to update slice of %s, ignoring: %s",
+                                  ur->user_name,
+                                  sd_bus_message_get_error(m)->message);
+
+                return 0;
+        }
+
+        log_debug("Successfully set slice parameters of %s.", ur->user_name);
+        return 0;
+}
+
+static int user_update_slice(User *u) {
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+        int r;
+
+        assert(u);
+
+        if (u->user_record->tasks_max == UINT64_MAX &&
+            u->user_record->memory_high == UINT64_MAX &&
+            u->user_record->memory_max == UINT64_MAX &&
+            u->user_record->cpu_weight == UINT64_MAX &&
+            u->user_record->io_weight == UINT64_MAX)
+                return 0;
+
+        r = sd_bus_message_new_method_call(
+                        u->manager->bus,
+                        &m,
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        "SetUnitProperties");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append(m, "sb", u->slice, true);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_open_container(m, 'a', "(sv)");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        const struct {
+                const char *name;
+                uint64_t value;
+        } settings[] = {
+                { "TasksMax",   u->user_record->tasks_max   },
+                { "MemoryMax",  u->user_record->memory_max  },
+                { "MemoryHigh", u->user_record->memory_high },
+                { "CPUWeight",  u->user_record->cpu_weight  },
+                { "IOWeight",   u->user_record->io_weight   },
+        };
+
+        for (size_t i = 0; i < ELEMENTSOF(settings); i++)
+                if (settings[i].value != UINT64_MAX) {
+                        r = sd_bus_message_append(m, "(sv)", settings[i].name, "t", settings[i].value);
+                        if (r < 0)
+                                return bus_log_create_error(r);
+                }
+
+        r = sd_bus_message_close_container(m);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_call_async(u->manager->bus, NULL, m, update_slice_callback, u->user_record, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to change user slice properties: %m");
+
+        /* Ref the user record pointer, so that the slot keeps it pinned */
+        user_record_ref(u->user_record);
+
+        return 0;
+}
+
 int user_start(User *u) {
         assert(u);
 
@@ -376,12 +450,15 @@ int user_start(User *u) {
         u->stopping = false;
 
         if (!u->started)
-                log_debug("Starting services for new user %s.", u->name);
+                log_debug("Starting services for new user %s.", u->user_record->user_name);
 
         /* Save the user data so far, because pam_systemd will read the XDG_RUNTIME_DIR out of it while starting up
          * systemd --user.  We need to do user_save_internal() because we have not "officially" started yet. */
         user_save_internal(u);
 
+        /* Set slice parameters */
+        (void) user_update_slice(u);
+
         /* Start user@UID.service */
         user_start_service(u);
 
@@ -460,7 +537,7 @@ int user_finalize(User *u) {
          * done. This is called as a result of an earlier user_done() when all jobs are completed. */
 
         if (u->started)
-                log_debug("User %s logged out.", u->name);
+                log_debug("User %s logged out.", u->user_record->user_name);
 
         LIST_FOREACH(sessions_by_user, s, u->sessions) {
                 k = session_finalize(s);
@@ -474,8 +551,8 @@ int user_finalize(User *u) {
          * cases, as we shouldn't accidentally remove a system service's IPC objects while it is running, just because
          * a cronjob running as the same user just finished. Hence: exclude system users generally from IPC clean-up,
          * and do it only for normal users. */
-        if (u->manager->remove_ipc && !uid_is_system(u->uid)) {
-                k = clean_ipc_by_uid(u->uid);
+        if (u->manager->remove_ipc && !uid_is_system(u->user_record->uid)) {
+                k = clean_ipc_by_uid(u->user_record->uid);
                 if (k < 0)
                         r = k;
         }
@@ -531,7 +608,7 @@ int user_check_linger_file(User *u) {
         _cleanup_free_ char *cc = NULL;
         char *p = NULL;
 
-        cc = cescape(u->name);
+        cc = cescape(u->user_record->user_name);
         if (!cc)
                 return -ENOMEM;
 
@@ -567,6 +644,18 @@ static bool user_unit_active(User *u) {
         return false;
 }
 
+static usec_t user_get_stop_delay(User *u) {
+        assert(u);
+
+        if (u->user_record->stop_delay_usec != UINT64_MAX)
+                return u->user_record->stop_delay_usec;
+
+        if (user_record_removable(u->user_record) > 0)
+                return 0; /* For removable users lower the stop delay to zero */
+
+        return u->manager->user_stop_delay;
+}
+
 bool user_may_gc(User *u, bool drop_not_started) {
         int r;
 
@@ -579,12 +668,16 @@ bool user_may_gc(User *u, bool drop_not_started) {
                 return false;
 
         if (u->last_session_timestamp != USEC_INFINITY) {
+                usec_t user_stop_delay;
+
                 /* All sessions have been closed. Let's see if we shall leave the user record around for a bit */
 
-                if (u->manager->user_stop_delay == USEC_INFINITY)
+                user_stop_delay = user_get_stop_delay(u);
+
+                if (user_stop_delay == USEC_INFINITY)
                         return false; /* Leave it around forever! */
-                if (u->manager->user_stop_delay > 0 &&
-                    now(CLOCK_MONOTONIC) < usec_add(u->last_session_timestamp, u->manager->user_stop_delay))
+                if (user_stop_delay > 0 &&
+                    now(CLOCK_MONOTONIC) < usec_add(u->last_session_timestamp, user_stop_delay))
                         return false; /* Leave it around for a bit longer. */
         }
 
@@ -712,7 +805,7 @@ void user_elect_display(User *u) {
 
         /* This elects a primary session for each user, which we call the "display". We try to keep the assignment
          * stable, but we "upgrade" to better choices. */
-        log_debug("Electing new display for user %s", u->name);
+        log_debug("Electing new display for user %s", u->user_record->user_name);
 
         LIST_FOREACH(sessions_by_user, s, u->sessions) {
                 if (!elect_display_filter(s)) {
@@ -737,6 +830,7 @@ static int user_stop_timeout_callback(sd_event_source *es, uint64_t usec, void *
 }
 
 void user_update_last_session_timer(User *u) {
+        usec_t user_stop_delay;
         int r;
 
         assert(u);
@@ -755,7 +849,8 @@ void user_update_last_session_timer(User *u) {
 
         assert(!u->timer_event_source);
 
-        if (IN_SET(u->manager->user_stop_delay, 0, USEC_INFINITY))
+        user_stop_delay = user_get_stop_delay(u);
+        if (IN_SET(user_stop_delay, 0, USEC_INFINITY))
                 return;
 
         if (sd_event_get_state(u->manager->event) == SD_EVENT_FINISHED) {
@@ -766,7 +861,7 @@ void user_update_last_session_timer(User *u) {
         r = sd_event_add_time(u->manager->event,
                               &u->timer_event_source,
                               CLOCK_MONOTONIC,
-                              usec_add(u->last_session_timestamp, u->manager->user_stop_delay), 0,
+                              usec_add(u->last_session_timestamp, user_stop_delay), 0,
                               user_stop_timeout_callback, u);
         if (r < 0)
                 log_warning_errno(r, "Failed to enqueue user stop event source, ignoring: %m");
@@ -775,8 +870,8 @@ void user_update_last_session_timer(User *u) {
                 char s[FORMAT_TIMESPAN_MAX];
 
                 log_debug("Last session of user '%s' logged out, terminating user context in %s.",
-                          u->name,
-                          format_timespan(s, sizeof(s), u->manager->user_stop_delay, USEC_PER_MSEC));
+                          u->user_record->user_name,
+                          format_timespan(s, sizeof(s), user_stop_delay, USEC_PER_MSEC));
         }
 }
 
index 4bd65d8373414348ee0d225d9ac206b12083d437..f8f172cb0f6d5ab4421a614b59a8dd3743890501 100644 (file)
@@ -6,6 +6,7 @@ typedef struct User User;
 #include "conf-parser.h"
 #include "list.h"
 #include "logind.h"
+#include "user-record.h"
 
 typedef enum UserState {
         USER_OFFLINE,    /* Not logged in at all */
@@ -20,10 +21,9 @@ typedef enum UserState {
 
 struct User {
         Manager *manager;
-        uid_t uid;
-        gid_t gid;
-        char *name;
-        char *home;
+
+        UserRecord *user_record;
+
         char *state_file;
         char *runtime_path;
 
@@ -50,7 +50,7 @@ struct User {
         LIST_FIELDS(User, gc_queue);
 };
 
-int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name, const char *home);
+int user_new(User **out, Manager *m, UserRecord *ur);
 User *user_free(User *u);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(User *, user_free);
index d8c1bbe15b95eec66d3ed11776a7821b1d405d2b..8f3708d2a4f00671d6dc8d0444315bfd6f9c3038 100644 (file)
@@ -9,7 +9,7 @@
 
 #include "alloc-util.h"
 #include "bus-error.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
 #include "cgroup-util.h"
 #include "def.h"
 #include "device-util.h"
index f0f8928ef2281e17988a8183a8bc3da7a5e64a3f..7e4673d27d3c28b851643b137362a94f208f4e9d 100644 (file)
@@ -12,6 +12,7 @@
 #include "list.h"
 #include "set.h"
 #include "time-util.h"
+#include "user-record.h"
 
 typedef struct Manager Manager;
 
@@ -28,7 +29,7 @@ struct Manager {
         Hashmap *seats;
         Hashmap *sessions;
         Hashmap *sessions_by_leader;
-        Hashmap *users;
+        Hashmap *users;  /* indexed by UID */
         Hashmap *inhibitors;
         Hashmap *buttons;
         Hashmap *brightness_writers;
@@ -130,7 +131,7 @@ int manager_add_device(Manager *m, const char *sysfs, bool master, Device **ret_
 int manager_add_button(Manager *m, const char *name, Button **ret_button);
 int manager_add_seat(Manager *m, const char *id, Seat **ret_seat);
 int manager_add_session(Manager *m, const char *id, Session **ret_session);
-int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, const char *home, User **ret_user);
+int manager_add_user(Manager *m, UserRecord *ur, User **ret_user);
 int manager_add_user_by_name(Manager *m, const char *name, User **ret_user);
 int manager_add_user_by_uid(Manager *m, uid_t uid, User **ret_user);
 int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **ret_inhibitor);
index 6dc79aa32aa4d875294d2ed2903dd1c9f83278ae..b726634cbf38b11f75643c9afaed4af62b849c77 100644 (file)
 
         <action id="org.freedesktop.login1.attach-device">
                 <description gettext-domain="systemd">Allow attaching devices to seats</description>
-                <message gettext-domain="systemd">Authentication is required for attaching a device to a seat.</message>
+                <message gettext-domain="systemd">Authentication is required to attach a device to a seat.</message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
 
         <action id="org.freedesktop.login1.flush-devices">
                 <description gettext-domain="systemd">Flush device to seat attachments</description>
-                <message gettext-domain="systemd">Authentication is required for resetting how devices are attached to seats.</message>
+                <message gettext-domain="systemd">Authentication is required to reset how devices are attached to seats.</message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
 
         <action id="org.freedesktop.login1.power-off">
                 <description gettext-domain="systemd">Power off the system</description>
-                <message gettext-domain="systemd">Authentication is required for powering off the system.</message>
+                <message gettext-domain="systemd">Authentication is required to power off the system.</message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
 
         <action id="org.freedesktop.login1.power-off-multiple-sessions">
                 <description gettext-domain="systemd">Power off the system while other users are logged in</description>
-                <message gettext-domain="systemd">Authentication is required for powering off the system while other users are logged in.</message>
+                <message gettext-domain="systemd">Authentication is required to power off the system while other users are logged in.</message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
         </action>
 
         <action id="org.freedesktop.login1.power-off-ignore-inhibit">
-                <description gettext-domain="systemd">Power off the system while an application asked to inhibit it</description>
-                <message gettext-domain="systemd">Authentication is required for powering off the system while an application asked to inhibit it.</message>
+                <description gettext-domain="systemd">Power off the system while an application is inhibiting this</description>
+                <message gettext-domain="systemd">Authentication is required to power off the system while an application is inhibiting this.</message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
 
         <action id="org.freedesktop.login1.reboot">
                 <description gettext-domain="systemd">Reboot the system</description>
-                <message gettext-domain="systemd">Authentication is required for rebooting the system.</message>
+                <message gettext-domain="systemd">Authentication is required to reboot the system.</message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
 
         <action id="org.freedesktop.login1.reboot-multiple-sessions">
                 <description gettext-domain="systemd">Reboot the system while other users are logged in</description>
-                <message gettext-domain="systemd">Authentication is required for rebooting the system while other users are logged in.</message>
+                <message gettext-domain="systemd">Authentication is required to reboot the system while other users are logged in.</message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
         </action>
 
         <action id="org.freedesktop.login1.reboot-ignore-inhibit">
-                <description gettext-domain="systemd">Reboot the system while an application asked to inhibit it</description>
-                <message gettext-domain="systemd">Authentication is required for rebooting the system while an application asked to inhibit it.</message>
+                <description gettext-domain="systemd">Reboot the system while an application is inhibiting this</description>
+                <message gettext-domain="systemd">Authentication is required to reboot the system while an application is inhibiting this.</message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
 
         <action id="org.freedesktop.login1.halt">
                 <description gettext-domain="systemd">Halt the system</description>
-                <message gettext-domain="systemd">Authentication is required for halting the system.</message>
+                <message gettext-domain="systemd">Authentication is required to halt the system.</message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
 
         <action id="org.freedesktop.login1.halt-multiple-sessions">
                 <description gettext-domain="systemd">Halt the system while other users are logged in</description>
-                <message gettext-domain="systemd">Authentication is required for halting the system while other users are logged in.</message>
+                <message gettext-domain="systemd">Authentication is required to halt the system while other users are logged in.</message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
         </action>
 
         <action id="org.freedesktop.login1.halt-ignore-inhibit">
-                <description gettext-domain="systemd">Halt the system while an application asked to inhibit it</description>
-                <message gettext-domain="systemd">Authentication is required for halting the system while an application asked to inhibit it.</message>
+                <description gettext-domain="systemd">Halt the system while an application is inhibiting this</description>
+                <message gettext-domain="systemd">Authentication is required to halt the system while an application is inhibiting this.</message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
 
         <action id="org.freedesktop.login1.suspend">
                 <description gettext-domain="systemd">Suspend the system</description>
-                <message gettext-domain="systemd">Authentication is required for suspending the system.</message>
+                <message gettext-domain="systemd">Authentication is required to suspend the system.</message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
 
         <action id="org.freedesktop.login1.suspend-multiple-sessions">
                 <description gettext-domain="systemd">Suspend the system while other users are logged in</description>
-                <message gettext-domain="systemd">Authentication is required for suspending the system while other users are logged in.</message>
+                <message gettext-domain="systemd">Authentication is required to suspend the system while other users are logged in.</message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
         </action>
 
         <action id="org.freedesktop.login1.suspend-ignore-inhibit">
-                <description gettext-domain="systemd">Suspend the system while an application asked to inhibit it</description>
-                <message gettext-domain="systemd">Authentication is required for suspending the system while an application asked to inhibit it.</message>
+                <description gettext-domain="systemd">Suspend the system while an application is inhibiting this</description>
+                <message gettext-domain="systemd">Authentication is required to suspend the system while an application is inhibiting this.</message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
 
         <action id="org.freedesktop.login1.hibernate">
                 <description gettext-domain="systemd">Hibernate the system</description>
-                <message gettext-domain="systemd">Authentication is required for hibernating the system.</message>
+                <message gettext-domain="systemd">Authentication is required to hibernate the system.</message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
 
         <action id="org.freedesktop.login1.hibernate-multiple-sessions">
                 <description gettext-domain="systemd">Hibernate the system while other users are logged in</description>
-                <message gettext-domain="systemd">Authentication is required for hibernating the system while other users are logged in.</message>
+                <message gettext-domain="systemd">Authentication is required to hibernate the system while other users are logged in.</message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
         </action>
 
         <action id="org.freedesktop.login1.hibernate-ignore-inhibit">
-                <description gettext-domain="systemd">Hibernate the system while an application asked to inhibit it</description>
-                <message gettext-domain="systemd">Authentication is required for hibernating the system while an application asked to inhibit it.</message>
+                <description gettext-domain="systemd">Hibernate the system while an application is inhibiting this</description>
+                <message gettext-domain="systemd">Authentication is required to hibernate the system while an application is inhibiting this.</message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
 
         <action id="org.freedesktop.login1.manage">
                 <description gettext-domain="systemd">Manage active sessions, users and seats</description>
-                <message gettext-domain="systemd">Authentication is required for managing active sessions, users and seats.</message>
+                <message gettext-domain="systemd">Authentication is required to manage active sessions, users and seats.</message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
                 </defaults>
         </action>
 
+        <action id="org.freedesktop.login1.chvt">
+                <description gettext-domain="systemd">Change Session</description>
+                <message gettext-domain="systemd">Authentication is required to change the virtual terminal.</message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>yes</allow_active>
+                </defaults>
+        </action>
+
 </policyconfig>
index aa6e5ea7aca847dc76439f32b88b7a12e5f84646..84bea21ab7be0777899a2d35c5b56cecaab00471 100644 (file)
@@ -11,6 +11,7 @@
 #include <security/pam_modutil.h>
 #include <sys/file.h>
 #include <sys/stat.h>
+#include <sys/sysmacros.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include "fd-util.h"
 #include "fileio.h"
 #include "format-util.h"
+#include "fs-util.h"
 #include "hostname-util.h"
+#include "locale-util.h"
 #include "login-util.h"
 #include "macro.h"
+#include "pam-util.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
+#include "rlimit-util.h"
 #include "socket-util.h"
 #include "stdio-util.h"
 #include "strv.h"
 #include "terminal-util.h"
+#include "user-util.h"
+#include "userdb.h"
 
 #define LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC (2*USEC_PER_MINUTE)
 
@@ -86,18 +93,15 @@ static int parse_argv(
         return 0;
 }
 
-static int get_user_data(
+static int acquire_user_record(
                 pam_handle_t *handle,
-                const char **ret_username,
-                struct passwd **ret_pw) {
+                UserRecord **ret_record) {
 
-        const char *username = NULL;
-        struct passwd *pw = NULL;
+        _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+        const char *username = NULL, *json = NULL;
         int r;
 
         assert(handle);
-        assert(ret_username);
-        assert(ret_pw);
 
         r = pam_get_user(handle, &username, NULL);
         if (r != PAM_SUCCESS) {
@@ -107,17 +111,75 @@ static int get_user_data(
 
         if (isempty(username)) {
                 pam_syslog(handle, LOG_ERR, "User name not valid.");
-                return PAM_AUTH_ERR;
+                return PAM_SERVICE_ERR;
         }
 
-        pw = pam_modutil_getpwnam(handle, username);
-        if (!pw) {
-                pam_syslog(handle, LOG_ERR, "Failed to get user data.");
-                return PAM_USER_UNKNOWN;
+        /* If pam_systemd_homed (or some other module) already acqired 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;
+                }
+
+                TAKE_PTR(formatted);
+        } else {
+                _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+                /* Parse cached record */
+                r = json_parse(json, JSON_PARSE_SENSITIVE, &v, NULL, NULL);
+                if (r < 0) {
+                        pam_syslog(handle, LOG_ERR, "Failed to parse JSON user record: %s", strerror_safe(r));
+                        return PAM_SERVICE_ERR;
+                }
+
+                ur = user_record_new();
+                if (!ur)
+                        return pam_log_oom(handle);
+
+                r = user_record_load(ur, v, USER_RECORD_LOAD_REFUSE_SECRET);
+                if (r < 0) {
+                        pam_syslog(handle, LOG_ERR, "Failed to load user record: %s", strerror_safe(r));
+                        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;
+                }
+        }
+
+        if (!uid_is_valid(ur->uid)) {
+                pam_syslog(handle, LOG_ERR, "Acquired user record does not have a UID.");
+                return PAM_SERVICE_ERR;
         }
 
-        *ret_pw = pw;
-        *ret_username = username;
+        if (ret_record)
+                *ret_record = TAKE_PTR(ur);
 
         return PAM_SUCCESS;
 }
@@ -157,11 +219,13 @@ static int socket_from_display(const char *display, char **path) {
 }
 
 static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) {
-        union sockaddr_union sa = {};
-        _cleanup_free_ char *p = NULL, *tty = NULL;
+        union sockaddr_union sa;
+        socklen_t sa_len;
+        _cleanup_free_ char *p = NULL, *sys_path = NULL, *tty = NULL;
         _cleanup_close_ int fd = -1;
         struct ucred ucred;
-        int v, r, salen;
+        int v, r;
+        dev_t display_ctty;
 
         assert(display);
         assert(vtnr);
@@ -175,22 +239,29 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_
         r = socket_from_display(display, &p);
         if (r < 0)
                 return r;
-        salen = sockaddr_un_set_path(&sa.un, p);
-        if (salen < 0)
-                return salen;
+        r = sockaddr_un_set_path(&sa.un, p);
+        if (r < 0)
+                return r;
+        sa_len = r;
 
         fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
         if (fd < 0)
                 return -errno;
 
-        if (connect(fd, &sa.sa, salen) < 0)
+        if (connect(fd, &sa.sa, sa_len) < 0)
                 return -errno;
 
         r = getpeercred(fd, &ucred);
         if (r < 0)
                 return r;
 
-        r = get_ctty(ucred.pid, NULL, &tty);
+        r = get_ctty_devnr(ucred.pid, &display_ctty);
+        if (r < 0)
+                return r;
+
+        if (asprintf(&sys_path, "/sys/dev/char/%d:%d", major(display_ctty), minor(display_ctty)) < 0)
+                return -ENOMEM;
+        r = readlink_value(sys_path, &tty);
         if (r < 0)
                 return r;
 
@@ -233,17 +304,15 @@ static int export_legacy_dbus_address(
                 return PAM_SUCCESS;
 
         if (asprintf(&t, DEFAULT_USER_BUS_ADDRESS_FMT, runtime) < 0)
-                goto error;
+                return pam_log_oom(handle);
 
         r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", t, 0);
-        if (r != PAM_SUCCESS)
-                goto error;
+        if (r != PAM_SUCCESS) {
+                pam_syslog(handle, LOG_ERR, "Failed to set bus variable: %s", pam_strerror(handle, r));
+                return r;
+        }
 
         return PAM_SUCCESS;
-
-error:
-        pam_syslog(handle, LOG_ERR, "Failed to set bus variable.");
-        return r;
 }
 
 static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
@@ -251,36 +320,36 @@ static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, co
         int r;
 
         if (isempty(limit))
-                return 0;
+                return PAM_SUCCESS;
 
         if (streq(limit, "infinity")) {
                 r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", (uint64_t)-1);
-                if (r < 0) {
-                        pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
-                        return r;
-                }
-        } else {
-                r = parse_permille(limit);
-                if (r >= 0) {
-                        r = sd_bus_message_append(m, "(sv)", "MemoryMaxScale", "u", (uint32_t) (((uint64_t) r * UINT32_MAX) / 1000U));
-                        if (r < 0) {
-                                pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
-                                return r;
-                        }
-                } else {
-                        r = parse_size(limit, 1024, &val);
-                        if (r >= 0) {
-                                r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", val);
-                                if (r < 0) {
-                                        pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
-                                        return r;
-                                }
-                        } else
-                                pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.memory_max: %s, ignoring.", limit);
-                }
+                if (r < 0)
+                        return pam_bus_log_create_error(handle, r);
+
+                return PAM_SUCCESS;
         }
 
-        return 0;
+        r = parse_permille(limit);
+        if (r >= 0) {
+                r = sd_bus_message_append(m, "(sv)", "MemoryMaxScale", "u", (uint32_t) (((uint64_t) r * UINT32_MAX) / 1000U));
+                if (r < 0)
+                        return pam_bus_log_create_error(handle, r);
+
+                return PAM_SUCCESS;
+        }
+
+        r = parse_size(limit, 1024, &val);
+        if (r >= 0) {
+                r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", val);
+                if (r < 0)
+                        return pam_bus_log_create_error(handle, r);
+
+                return PAM_SUCCESS;
+        }
+
+        pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.memory_max, ignoring: %s", limit);
+        return PAM_SUCCESS;
 }
 
 static int append_session_runtime_max_sec(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
@@ -289,19 +358,17 @@ static int append_session_runtime_max_sec(pam_handle_t *handle, sd_bus_message *
 
         /* No need to parse "infinity" here, it will be set by default later in scope_init() */
         if (isempty(limit) || streq(limit, "infinity"))
-                return 0;
+                return PAM_SUCCESS;
 
         r = parse_sec(limit, &val);
         if (r >= 0) {
                 r = sd_bus_message_append(m, "(sv)", "RuntimeMaxUSec", "t", (uint64_t) val);
-                if (r < 0) {
-                        pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
-                        return r;
-                }
+                if (r < 0)
+                        return pam_bus_log_create_error(handle, r);
         } else
                 pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.runtime_max_sec: %s, ignoring.", limit);
 
-        return 0;
+        return PAM_SUCCESS;
 }
 
 static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
@@ -310,19 +377,17 @@ static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, con
 
         /* No need to parse "infinity" here, it will be set unconditionally later in manager_start_scope() */
         if (isempty(limit) || streq(limit, "infinity"))
-                return 0;
+                return PAM_SUCCESS;
 
         r = safe_atou64(limit, &val);
         if (r >= 0) {
                 r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", val);
-                if (r < 0) {
-                        pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
-                        return r;
-                }
+                if (r < 0)
+                        return pam_bus_log_create_error(handle, r);
         } else
-                pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.tasks_max: %s, ignoring.", limit);
+                pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.tasks_max, ignoring: %s", limit);
 
-        return 0;
+        return PAM_SUCCESS;
 }
 
 static int append_session_cg_weight(pam_handle_t *handle, sd_bus_message *m, const char *limit, const char *field) {
@@ -330,21 +395,19 @@ static int append_session_cg_weight(pam_handle_t *handle, sd_bus_message *m, con
         int r;
 
         if (isempty(limit))
-                return 0;
+                return PAM_SUCCESS;
 
         r = cg_weight_parse(limit, &val);
         if (r >= 0) {
                 r = sd_bus_message_append(m, "(sv)", field, "t", val);
-                if (r < 0) {
-                        pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
-                        return r;
-                }
+                if (r < 0)
+                        return pam_bus_log_create_error(handle, r);
         } else if (streq(field, "CPUWeight"))
-                pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.cpu_weight: %s, ignoring.", limit);
+                pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.cpu_weight, ignoring: %s", limit);
         else
-                pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.io_weight: %s, ignoring.", limit);
+                pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.io_weight, ignoring: %s", limit);
 
-        return 0;
+        return PAM_SUCCESS;
 }
 
 static const char* getenv_harder(pam_handle_t *handle, const char *key, const char *fallback) {
@@ -423,6 +486,138 @@ fail:
         return false;
 }
 
+static int pam_putenv_and_log(pam_handle_t *handle, const char *e, bool debug) {
+        int r;
+
+        assert(handle);
+        assert(e);
+
+        r = pam_putenv(handle, e);
+        if (r != PAM_SUCCESS) {
+                pam_syslog(handle, LOG_ERR, "Failed to set PAM environment variable %s: %s", e, pam_strerror(handle, r));
+                return r;
+        }
+
+        if (debug)
+                pam_syslog(handle, LOG_DEBUG, "PAM environment variable %s set based on user record.", e);
+
+        return PAM_SUCCESS;
+}
+
+static int apply_user_record_settings(pam_handle_t *handle, UserRecord *ur, bool debug) {
+        char **i;
+        int r;
+
+        assert(handle);
+        assert(ur);
+
+        if (ur->umask != MODE_INVALID) {
+                umask(ur->umask);
+
+                if (debug)
+                        pam_syslog(handle, LOG_DEBUG, "Set user umask to %04o based on user record.", ur->umask);
+        }
+
+        STRV_FOREACH(i, ur->environment) {
+                _cleanup_free_ char *n = NULL;
+                const char *e;
+
+                assert_se(e = strchr(*i, '=')); /* environment was already validated while parsing JSON record, this thus must hold */
+
+                n = strndup(*i, e - *i);
+                if (!n)
+                        return pam_log_oom(handle);
+
+                if (pam_getenv(handle, n)) {
+                        if (debug)
+                                pam_syslog(handle, LOG_DEBUG, "PAM environment variable $%s already set, not changing based on record.", *i);
+                        continue;
+                }
+
+                r = pam_putenv_and_log(handle, *i, debug);
+                if (r != PAM_SUCCESS)
+                        return r;
+        }
+
+        if (ur->email_address) {
+                if (pam_getenv(handle, "EMAIL")) {
+                        if (debug)
+                                pam_syslog(handle, LOG_DEBUG, "PAM environment variable $EMAIL already set, not changing based on user record.");
+                } else {
+                        _cleanup_free_ char *joined = NULL;
+
+                        joined = strjoin("EMAIL=", ur->email_address);
+                        if (!joined)
+                                return pam_log_oom(handle);
+
+                        r = pam_putenv_and_log(handle, joined, debug);
+                        if (r != PAM_SUCCESS)
+                                return r;
+                }
+        }
+
+        if (ur->time_zone) {
+                if (pam_getenv(handle, "TZ")) {
+                        if (debug)
+                                pam_syslog(handle, LOG_DEBUG, "PAM environment variable $TZ already set, not changing based on user record.");
+                } else if (!timezone_is_valid(ur->time_zone, LOG_DEBUG)) {
+                        if (debug)
+                                pam_syslog(handle, LOG_DEBUG, "Time zone specified in user record is not valid locally, not setting $TZ.");
+                } else {
+                        _cleanup_free_ char *joined = NULL;
+
+                        joined = strjoin("TZ=:", ur->time_zone);
+                        if (!joined)
+                                return pam_log_oom(handle);
+
+                        r = pam_putenv_and_log(handle, joined, debug);
+                        if (r != PAM_SUCCESS)
+                                return r;
+                }
+        }
+
+        if (ur->preferred_language) {
+                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)) {
+                        if (debug)
+                                pam_syslog(handle, LOG_DEBUG, "Preferred language specified in user record is not valid locally, not setting $LANG.");
+                } else {
+                        _cleanup_free_ char *joined = NULL;
+
+                        joined = strjoin("LANG=", ur->preferred_language);
+                        if (!joined)
+                                return pam_log_oom(handle);
+
+                        r = pam_putenv_and_log(handle, joined, debug);
+                        if (r != PAM_SUCCESS)
+                                return r;
+                }
+        }
+
+        if (nice_is_valid(ur->nice_level)) {
+                if (nice(ur->nice_level) < 0)
+                        pam_syslog(handle, LOG_ERR, "Failed to set nice level to %i, ignoring: %s", ur->nice_level, strerror_safe(errno));
+                else if (debug)
+                        pam_syslog(handle, LOG_DEBUG, "Nice level set, based on user record.");
+        }
+
+        for (int rl = 0; rl < _RLIMIT_MAX; rl++) {
+
+                if (!ur->rlimits[rl])
+                        continue;
+
+                r = setrlimit_closest(rl, ur->rlimits[rl]);
+                if (r < 0)
+                        pam_syslog(handle, LOG_ERR, "Failed to set resource limit %s, ignoring: %s", rlimit_to_string(rl), strerror_safe(r));
+                else if (debug)
+                        pam_syslog(handle, LOG_DEBUG, "Resource limit %s set, based on user record.", rlimit_to_string(rl));
+        }
+
+        return PAM_SUCCESS;
+}
+
 _public_ PAM_EXTERN int pam_sm_open_session(
                 pam_handle_t *handle,
                 int flags,
@@ -431,7 +626,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
         const char
-                *username, *id, *object_path, *runtime_path,
+                *id, *object_path, *runtime_path,
                 *service = NULL,
                 *tty = NULL, *display = NULL,
                 *remote_user = NULL, *remote_host = NULL,
@@ -440,9 +635,9 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 *class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL, *desktop_pam = NULL,
                 *memory_max = NULL, *tasks_max = NULL, *cpu_weight = NULL, *io_weight = NULL, *runtime_max_sec = NULL;
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
         int session_fd = -1, existing, r;
         bool debug = false, remote;
-        struct passwd *pw;
         uint32_t vtnr = 0;
         uid_t original_uid;
 
@@ -463,7 +658,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         if (debug)
                 pam_syslog(handle, LOG_DEBUG, "pam-systemd initializing");
 
-        r = get_user_data(handle, &username, &pw);
+        r = acquire_user_record(handle, &ur);
         if (r != PAM_SUCCESS)
                 return r;
 
@@ -477,8 +672,8 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         if (streq_ptr(service, "systemd-user")) {
                 char rt[STRLEN("/run/user/") + DECIMAL_STR_MAX(uid_t)];
 
-                xsprintf(rt, "/run/user/"UID_FMT, pw->pw_uid);
-                if (validate_runtime_directory(handle, rt, pw->pw_uid)) {
+                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));
@@ -486,7 +681,11 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                         }
                 }
 
-                r = export_legacy_dbus_address(handle, pw->pw_uid, rt);
+                r = export_legacy_dbus_address(handle, ur->uid, rt);
+                if (r != PAM_SUCCESS)
+                        return r;
+
+                r = apply_user_record_settings(handle, ur, debug);
                 if (r != PAM_SUCCESS)
                         return r;
 
@@ -573,16 +772,14 @@ _public_ PAM_EXTERN int pam_sm_open_session(
 
         /* Talk to logind over the message bus */
 
-        r = sd_bus_open_system(&bus);
-        if (r < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror_safe(r));
-                return PAM_SESSION_ERR;
-        }
+        r = pam_acquire_bus_connection(handle, &bus);
+        if (r != PAM_SUCCESS)
+                return r;
 
         if (debug) {
                 pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: "
                            "uid="UID_FMT" pid="PID_FMT" service=%s type=%s class=%s desktop=%s seat=%s vtnr=%"PRIu32" tty=%s display=%s remote=%s remote_user=%s remote_host=%s",
-                           pw->pw_uid, getpid_cached(),
+                           ur->uid, getpid_cached(),
                            strempty(service),
                            type, class, strempty(desktop),
                            strempty(seat), vtnr, strempty(tty), strempty(display),
@@ -599,13 +796,11 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                         "/org/freedesktop/login1",
                         "org.freedesktop.login1.Manager",
                         "CreateSession");
-        if (r < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to create CreateSession method call: %s", strerror_safe(r));
-                return PAM_SESSION_ERR;
-        }
+        if (r < 0)
+                return pam_bus_log_create_error(handle, r);
 
         r = sd_bus_message_append(m, "uusssssussbss",
-                        (uint32_t) pw->pw_uid,
+                        (uint32_t) ur->uid,
                         0,
                         service,
                         type,
@@ -618,42 +813,36 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                         remote,
                         remote_user,
                         remote_host);
-        if (r < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
-                return PAM_SESSION_ERR;
-        }
+        if (r < 0)
+                return pam_bus_log_create_error(handle, r);
 
         r = sd_bus_message_open_container(m, 'a', "(sv)");
-        if (r < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to open message container: %s", strerror_safe(r));
-                return PAM_SYSTEM_ERR;
-        }
+        if (r < 0)
+                return pam_bus_log_create_error(handle, r);
 
         r = append_session_memory_max(handle, m, memory_max);
-        if (r < 0)
-                return PAM_SESSION_ERR;
+        if (r != PAM_SUCCESS)
+                return r;
 
         r = append_session_runtime_max_sec(handle, m, runtime_max_sec);
-        if (r < 0)
-                return PAM_SESSION_ERR;
+        if (r != PAM_SUCCESS)
+                return r;
 
         r = append_session_tasks_max(handle, m, tasks_max);
-        if (r < 0)
-                return PAM_SESSION_ERR;
+        if (r != PAM_SUCCESS)
+                return r;
 
         r = append_session_cg_weight(handle, m, cpu_weight, "CPUWeight");
-        if (r < 0)
-                return PAM_SESSION_ERR;
+        if (r != PAM_SUCCESS)
+                return r;
 
         r = append_session_cg_weight(handle, m, io_weight, "IOWeight");
-        if (r < 0)
-                return PAM_SESSION_ERR;
+        if (r != PAM_SUCCESS)
+                return r;
 
         r = sd_bus_message_close_container(m);
-        if (r < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to close message container: %s", strerror_safe(r));
-                return PAM_SYSTEM_ERR;
-        }
+        if (r < 0)
+                return pam_bus_log_create_error(handle, r);
 
         r = sd_bus_call(bus, m, LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
         if (r < 0) {
@@ -677,10 +866,8 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                                 &seat,
                                 &vtnr,
                                 &existing);
-        if (r < 0) {
-                pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", strerror_safe(r));
-                return PAM_SESSION_ERR;
-        }
+        if (r < 0)
+                return pam_bus_log_parse_error(handle, r);
 
         if (debug)
                 pam_syslog(handle, LOG_DEBUG, "Reply from logind: "
@@ -691,20 +878,20 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         if (r != PAM_SUCCESS)
                 return r;
 
-        if (original_uid == pw->pw_uid) {
+        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, pw->pw_uid)) {
+                if (validate_runtime_directory(handle, runtime_path, ur->uid)) {
                         r = update_environment(handle, "XDG_RUNTIME_DIR", runtime_path);
                         if (r != PAM_SUCCESS)
                                 return r;
                 }
 
-                r = export_legacy_dbus_address(handle, pw->pw_uid, runtime_path);
+                r = export_legacy_dbus_address(handle, ur->uid, runtime_path);
                 if (r != PAM_SUCCESS)
                         return r;
         }
@@ -759,6 +946,14 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 }
         }
 
+        r = apply_user_record_settings(handle, ur, debug);
+        if (r != PAM_SUCCESS)
+                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 use the bus connection in that time, so let's better close before the daemon kicks us
+         * off because we are not processing anything. */
+        (void) pam_release_bus_connection(handle);
         return PAM_SUCCESS;
 }
 
@@ -767,8 +962,6 @@ _public_ PAM_EXTERN int pam_sm_close_session(
                 int flags,
                 int argc, const char **argv) {
 
-        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         const void *existing = NULL;
         const char *id;
         int r;
@@ -781,17 +974,15 @@ _public_ PAM_EXTERN int pam_sm_close_session(
 
         id = pam_getenv(handle, "XDG_SESSION_ID");
         if (id && !existing) {
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+                _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
 
-                /* Before we go and close the FIFO we need to tell
-                 * logind that this is a clean session shutdown, so
-                 * that it doesn't just go and slaughter us
-                 * immediately after closing the fd */
+                /* Before we go and close the FIFO we need to tell logind that this is a clean session
+                 * shutdown, so that it doesn't just go and slaughter us immediately after closing the fd */
 
-                r = sd_bus_open_system(&bus);
-                if (r < 0) {
-                        pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror_safe(r));
-                        return PAM_SESSION_ERR;
-                }
+                r = pam_acquire_bus_connection(handle, &bus);
+                if (r != PAM_SUCCESS)
+                        return r;
 
                 r = sd_bus_call_method(bus,
                                        "org.freedesktop.login1",
@@ -808,11 +999,9 @@ _public_ PAM_EXTERN int pam_sm_close_session(
                 }
         }
 
-        /* Note that we are knowingly leaking the FIFO fd here. This
-         * way, logind can watch us die. If we closed it here it would
-         * not have any clue when that is completed. Given that one
-         * cannot really have multiple PAM sessions open from the same
-         * process this means we will leak one FD at max. */
+        /* Note that we are knowingly leaking the FIFO fd here. This way, logind can watch us die. If we
+         * closed it here it would not have any clue when that is completed. Given that one cannot really
+         * have multiple PAM sessions open from the same process this means we will leak one FD at max. */
 
         return PAM_SUCCESS;
 }
index b45355d86fce7d322c2ee9bf8f33682b791d6aa8..294ef349321f4c0397d9d9aecc9a3dfa8b1484b6 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "alloc-util.h"
 #include "bus-label.h"
+#include "bus-polkit.h"
 #include "bus-util.h"
 #include "copy.h"
 #include "dissect-image.h"
index 3b2ac3829859618b633587d21864136cf82339ea..a2990452af17973de8844844f13bdb5c575ac0b9 100644 (file)
@@ -14,6 +14,7 @@
 #include "bus-common-errors.h"
 #include "bus-internal.h"
 #include "bus-label.h"
+#include "bus-polkit.h"
 #include "bus-util.h"
 #include "copy.h"
 #include "env-file.h"
index 2377416b4406a034549ff346a6f9b1dc48de0b68..61234582a3405e50b3700bb7926f256c86e66d77 100644 (file)
@@ -626,6 +626,7 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
                         show_journal_by_unit(
                                         stdout,
                                         i->unit,
+                                        NULL,
                                         arg_output,
                                         0,
                                         i->timestamp.monotonic,
index 6fc3b930570be25e6c9ac4c2efe590228d4bfc92..d0cc07678fa25e4193c1590dbef32fbace3bf7f2 100644 (file)
@@ -8,6 +8,7 @@
 #include "alloc-util.h"
 #include "btrfs-util.h"
 #include "bus-common-errors.h"
+#include "bus-polkit.h"
 #include "bus-util.h"
 #include "cgroup-util.h"
 #include "errno-util.h"
index a3bed035dc80c95169491506e078f1514f9f9c62..ace2131c2deaa512bb9960d73277dd2093b0fe3c 100644 (file)
@@ -10,7 +10,7 @@
 
 #include "alloc-util.h"
 #include "bus-error.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
 #include "cgroup-util.h"
 #include "dirent-util.h"
 #include "fd-util.h"
index 77d9cbb452d2eaa76170400b4569454632246ae7..982e0a285e386e97b1a539413ba335e55001253f 100644 (file)
@@ -1403,7 +1403,7 @@ static int list_devices(void) {
         if (arg_full)
                 table_set_width(table, 0);
 
-        r = table_set_sort(table, 0, SIZE_MAX);
+        r = table_set_sort(table, (size_t) 0, (size_t) SIZE_MAX);
         if (r < 0)
                 return log_error_errno(r, "Failed to set sort index: %m");
 
index 7cb1bb5a388a82fbbb94698047f68681e5850597..57d3772c3ed174312e97549bcb5631a3829b6948 100644 (file)
@@ -107,22 +107,38 @@ sources = files('''
         networkd-util.h
         networkd-wifi.c
         networkd-wifi.h
+        tc/cake.c
+        tc/cake.h
         tc/codel.c
         tc/codel.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/htb.c
+        tc/htb.h
         tc/netem.c
         tc/netem.h
         tc/qdisc.c
         tc/qdisc.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())
 
 systemd_networkd_sources = files('networkd.c')
index 8b0e3d27eae3194510802c6c50dac03fee5ba060..72b33ed9e59302f1a7b92e9d845097d410644042 100644 (file)
@@ -729,7 +729,7 @@ static void ipip_sit_init(NetDev *n) {
         assert(t);
 
         t->pmtudisc = true;
-        t->fou_encap_type = FOU_ENCAP_DIRECT;
+        t->fou_encap_type = NETDEV_FOO_OVER_UDP_ENCAP_DIRECT;
         t->isatap = -1;
 }
 
@@ -771,7 +771,7 @@ static void gre_erspan_init(NetDev *n) {
 
         t->pmtudisc = true;
         t->gre_erspan_sequence = -1;
-        t->fou_encap_type = FOU_ENCAP_DIRECT;
+        t->fou_encap_type = NETDEV_FOO_OVER_UDP_ENCAP_DIRECT;
 }
 
 static void ip6gre_init(NetDev *n) {
index 631b12cf51a9d1833999d2914f48d766fb781fdb..880917a84421ae51f27d6fef7559a0c65ba5ddd0 100644 (file)
@@ -16,7 +16,6 @@
 #include "sd-network.h"
 
 #include "alloc-util.h"
-#include "arphrd-list.h"
 #include "bus-common-errors.h"
 #include "bus-error.h"
 #include "bus-util.h"
@@ -27,6 +26,7 @@
 #include "fd-util.h"
 #include "format-table.h"
 #include "format-util.h"
+#include "glob-util.h"
 #include "hwdb-util.h"
 #include "local-addresses.h"
 #include "locale-util.h"
@@ -34,6 +34,7 @@
 #include "macro.h"
 #include "main-func.h"
 #include "netlink-util.h"
+#include "network-internal.h"
 #include "pager.h"
 #include "parse-util.h"
 #include "pretty-print.h"
@@ -65,27 +66,6 @@ static bool arg_stats = false;
 static bool arg_full = false;
 static unsigned arg_lines = 10;
 
-static char *link_get_type_string(unsigned short iftype, sd_device *d) {
-        const char *t, *devtype;
-        char *p;
-
-        if (d &&
-            sd_device_get_devtype(d, &devtype) >= 0 &&
-            !isempty(devtype))
-                return strdup(devtype);
-
-        t = arphrd_to_name(iftype);
-        if (!t)
-                return NULL;
-
-        p = strdup(t);
-        if (!p)
-                return NULL;
-
-        ascii_strlower(p);
-        return p;
-}
-
 static void operational_state_to_color(const char *name, const char *state, const char **on, const char **off) {
         assert(on);
         assert(off);
@@ -169,7 +149,7 @@ typedef struct LinkInfo {
 
         /* ethtool info */
         int autonegotiation;
-        size_t speed;
+        uint64_t speed;
         Duplex duplex;
         NetDevPort port;
 
@@ -267,7 +247,7 @@ static int decode_netdev(sd_netlink_message *m, LinkInfo *info) {
         return 0;
 }
 
-static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns) {
+static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns, bool matched_patterns[]) {
         _cleanup_strv_free_ char **altnames = NULL;
         const char *name;
         int ifindex, r;
@@ -297,20 +277,26 @@ static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns) {
 
         if (patterns) {
                 char str[DECIMAL_STR_MAX(int)];
+                size_t pos;
+
+                assert(matched_patterns);
 
                 xsprintf(str, "%i", ifindex);
-                if (!strv_fnmatch(patterns, str, 0) && !strv_fnmatch(patterns, name, 0)) {
+                if (!strv_fnmatch_full(patterns, str, 0, &pos) &&
+                    !strv_fnmatch_full(patterns, name, 0, &pos)) {
                         bool match = false;
                         char **p;
 
                         STRV_FOREACH(p, altnames)
-                                if (strv_fnmatch(patterns, *p, 0)) {
+                                if (strv_fnmatch_full(patterns, *p, 0, &pos)) {
                                         match = true;
                                         break;
                                 }
                         if (!match)
                                 return 0;
                 }
+
+                matched_patterns[pos] = true;
         }
 
         r = sd_rtnl_message_link_get_type(m, &info->iftype);
@@ -465,11 +451,18 @@ static int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char **patterns, Lin
         if (r < 0)
                 return log_error_errno(r, "Failed to enumerate links: %m");
 
+        _cleanup_free_ bool *matched_patterns = NULL;
+        if (patterns) {
+                matched_patterns = new0(bool, strv_length(patterns));
+                if (!matched_patterns)
+                        return log_oom();
+        }
+
         for (i = reply; i; i = sd_netlink_message_next(i)) {
                 if (!GREEDY_REALLOC0(links, allocated, c + 2)) /* We keep one trailing one as marker */
                         return -ENOMEM;
 
-                r = decode_link(i, links + c, patterns);
+                r = decode_link(i, links + c, patterns, matched_patterns);
                 if (r < 0)
                         return r;
                 if (r == 0)
@@ -487,6 +480,20 @@ static int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char **patterns, Lin
                 c++;
         }
 
+        /* Look if we matched all our arguments that are not globs. It
+         * is OK for a glob to match nothing, but not for an exact argument. */
+        for (size_t pos = 0; pos < strv_length(patterns); pos++) {
+                if (matched_patterns[pos])
+                        continue;
+
+                if (string_is_glob(patterns[pos]))
+                        log_debug("Pattern \"%s\" doesn't match any interface, ignoring.",
+                                  patterns[pos]);
+                else
+                        return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
+                                               "Interface \"%s\" not found.", patterns[pos]);
+        }
+
         typesafe_qsort(links, c, link_info_compare);
 
         if (bus)
@@ -495,6 +502,9 @@ static int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char **patterns, Lin
 
         *ret = TAKE_PTR(links);
 
+        if (patterns && c == 0)
+                log_warning("No interfaces matched.");
+
         return (int) c;
 }
 
@@ -711,29 +721,41 @@ static int get_gateway_description(
         return -ENODATA;
 }
 
+static int dump_list(Table *table, const char *prefix, char * const *l) {
+        int r;
+
+        if (strv_isempty(l))
+                return 0;
+
+        r = table_add_many(table,
+                           TABLE_EMPTY,
+                           TABLE_STRING, prefix,
+                           TABLE_STRV, l);
+        if (r < 0)
+                return table_log_add_error(r);
+
+        return 0;
+}
+
 static int dump_gateways(
                 sd_netlink *rtnl,
                 sd_hwdb *hwdb,
                 Table *table,
                 int ifindex) {
         _cleanup_free_ struct local_address *local = NULL;
+        _cleanup_strv_free_ char **buf = NULL;
         int r, n, i;
 
         assert(rtnl);
         assert(table);
 
         n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local);
-        if (n < 0)
+        if (n <= 0)
                 return n;
 
         for (i = 0; i < n; i++) {
                 _cleanup_free_ char *gateway = NULL, *description = NULL, *with_description = NULL;
-
-                r = table_add_many(table,
-                                   TABLE_EMPTY,
-                                   TABLE_STRING, i == 0 ? "Gateway:" : "");
-                if (r < 0)
-                        return table_log_add_error(r);
+                char name[IF_NAMESIZE+1];
 
                 r = in_addr_to_string(local[i].family, &local[i].address, &gateway);
                 if (r < 0)
@@ -741,28 +763,24 @@ static int dump_gateways(
 
                 r = get_gateway_description(rtnl, hwdb, local[i].ifindex, local[i].family, &local[i].address, &description);
                 if (r < 0)
-                        log_debug_errno(r, "Could not get description of gateway: %m");
+                        log_debug_errno(r, "Could not get description of gateway, ignoring: %m");
 
                 if (description) {
                         with_description = strjoin(gateway, " (", description, ")");
                         if (!with_description)
-                                return -ENOMEM;
+                                return log_oom();
                 }
 
-                /* Show interface name for the entry if we show
-                 * entries for all interfaces */
-                if (ifindex <= 0) {
-                        char name[IF_NAMESIZE+1];
-
-                        r = table_add_cell_stringf(table, NULL, "%s on %s", with_description ?: gateway,
-                                                   format_ifname_full(local[i].ifindex, name, FORMAT_IFNAME_IFINDEX_WITH_PERCENT));
-                } else
-                        r = table_add_cell(table, NULL, TABLE_STRING, with_description ?: gateway);
+                /* Show interface name for the entry if we show entries for all interfaces */
+                r = strv_extendf(&buf, "%s%s%s",
+                                 with_description ?: gateway,
+                                 ifindex <= 0 ? " on " : "",
+                                 ifindex <= 0 ? format_ifname_full(local[i].ifindex, name, FORMAT_IFNAME_IFINDEX_WITH_PERCENT) : "");
                 if (r < 0)
-                        return table_log_add_error(r);
+                        return log_oom();
         }
 
-        return 0;
+        return dump_list(table, "Gateway:", buf);
 }
 
 static int dump_addresses(
@@ -772,25 +790,21 @@ static int dump_addresses(
 
         _cleanup_free_ struct local_address *local = NULL;
         _cleanup_free_ char *dhcp4_address = NULL;
+        _cleanup_strv_free_ char **buf = NULL;
         int r, n, i;
 
         assert(rtnl);
         assert(table);
 
         n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local);
-        if (n < 0)
+        if (n <= 0)
                 return n;
 
         (void) sd_network_link_get_dhcp4_address(ifindex, &dhcp4_address);
 
         for (i = 0; i < n; i++) {
                 _cleanup_free_ char *pretty = NULL;
-
-                r = table_add_many(table,
-                                   TABLE_EMPTY,
-                                   TABLE_STRING, i == 0 ? "Address:" : "");
-                if (r < 0)
-                        return table_log_add_error(r);
+                char name[IF_NAMESIZE+1];
 
                 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
                 if (r < 0)
@@ -805,18 +819,15 @@ static int dump_addresses(
                                 return log_oom();
                 }
 
-                if (ifindex <= 0) {
-                        char name[IF_NAMESIZE+1];
-
-                        r = table_add_cell_stringf(table, NULL, "%s on %s", pretty,
-                                                   format_ifname_full(local[i].ifindex, name, FORMAT_IFNAME_IFINDEX_WITH_PERCENT));
-                } else
-                        r = table_add_cell(table, NULL, TABLE_STRING, pretty);
+                r = strv_extendf(&buf, "%s%s%s",
+                                 pretty,
+                                 ifindex <= 0 ? " on " : "",
+                                 ifindex <= 0 ? format_ifname_full(local[i].ifindex, name, FORMAT_IFNAME_IFINDEX_WITH_PERCENT) : "");
                 if (r < 0)
-                        return table_log_add_error(r);
+                        return log_oom();
         }
 
-        return 0;
+        return dump_list(table, "Address:", buf);
 }
 
 static int dump_address_labels(sd_netlink *rtnl) {
@@ -847,7 +858,7 @@ static int dump_address_labels(sd_netlink *rtnl) {
         if (arg_full)
                 table_set_width(table, 0);
 
-        r = table_set_sort(table, 0, SIZE_MAX);
+        r = table_set_sort(table, (size_t) 0, (size_t) SIZE_MAX);
         if (r < 0)
                 return r;
 
@@ -966,8 +977,9 @@ static int next_lldp_neighbor(FILE *f, sd_lldp_neighbor **ret) {
 }
 
 static int dump_lldp_neighbors(Table *table, const char *prefix, int ifindex) {
+        _cleanup_strv_free_ char **buf = NULL;
         _cleanup_fclose_ FILE *f = NULL;
-        int r, c = 0;
+        int r;
 
         assert(table);
         assert(prefix);
@@ -989,29 +1001,21 @@ static int dump_lldp_neighbors(Table *table, const char *prefix, int ifindex) {
                 if (r == 0)
                         break;
 
-                r = table_add_many(table,
-                                   TABLE_EMPTY,
-                                   TABLE_STRING, c == 0 ? prefix : "");
-                if (r < 0)
-                        return table_log_add_error(r);
-
                 (void) sd_lldp_neighbor_get_system_name(n, &system_name);
                 (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
                 (void) sd_lldp_neighbor_get_port_description(n, &port_description);
 
-                r = table_add_cell_stringf(table, NULL,
-                                           "%s on port %s%s%s%s",
-                                           strna(system_name), strna(port_id),
-                                           isempty(port_description) ? "" : " (",
-                                           strempty(port_description),
-                                           isempty(port_description) ? "" : ")");
+                r = strv_extendf(&buf, "%s on port %s%s%s%s",
+                                 strna(system_name),
+                                 strna(port_id),
+                                 isempty(port_description) ? "" : " (",
+                                 strempty(port_description),
+                                 isempty(port_description) ? "" : ")");
                 if (r < 0)
-                        return table_log_add_error(r);
-
-                c++;
+                        return log_oom();
         }
 
-        return c;
+        return dump_list(table, prefix, buf);
 }
 
 static int dump_ifindexes(Table *table, const char *prefix, const int *ifindexes) {
@@ -1035,25 +1039,6 @@ static int dump_ifindexes(Table *table, const char *prefix, const int *ifindexes
         return 0;
 }
 
-static int dump_list(Table *table, const char *prefix, char **l) {
-        char **i;
-        int r;
-
-        if (strv_isempty(l))
-                return 0;
-
-        STRV_FOREACH(i, l) {
-                r = table_add_many(table,
-                                   TABLE_EMPTY,
-                                   TABLE_STRING, i == l ? prefix : "",
-                                   TABLE_STRING, *i);
-                if (r < 0)
-                        return table_log_add_error(r);
-        }
-
-        return 0;
-}
-
 #define DUMP_STATS_ONE(name, val_name)                                  \
         r = table_add_many(table,                                       \
                            TABLE_EMPTY,                                 \
@@ -1166,7 +1151,6 @@ static int link_status_one(
         _cleanup_free_ int *carrier_bound_to = NULL, *carrier_bound_by = NULL;
         _cleanup_(table_unrefp) Table *table = NULL;
         TableCell *cell;
-        char **p;
         int r;
 
         assert(rtnl);
@@ -1251,20 +1235,9 @@ static int link_status_one(
         if (r < 0)
                 return table_log_add_error(r);
 
-        STRV_FOREACH(p, info->alternative_names) {
-                if (p == info->alternative_names)
-                        r = table_add_many(table,
-                                           TABLE_EMPTY,
-                                           TABLE_STRING, "Alternative Names:",
-                                           TABLE_STRING, *p);
-                else
-                        r = table_add_many(table,
-                                           TABLE_EMPTY,
-                                           TABLE_EMPTY,
-                                           TABLE_STRING, *p);
-                if (r < 0)
-                        return table_log_add_error(r);
-        }
+        r = dump_list(table, "Alternative Names:", info->alternative_names);
+        if (r < 0)
+                return r;
 
         if (path) {
                 r = table_add_many(table,
@@ -1501,7 +1474,7 @@ static int link_status_one(
                         r = table_add_many(table,
                                            TABLE_EMPTY,
                                            TABLE_STRING, "Speed:",
-                                           TABLE_BPS, (uint64_t) info->speed);
+                                           TABLE_BPS, info->speed);
                         if (r < 0)
                                 return table_log_add_error(r);
                 }
@@ -1970,6 +1943,48 @@ 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 = sd_bus_call_method(
+                        bus,
+                        "org.freedesktop.network1",
+                        "/org/freedesktop/network1",
+                        "org.freedesktop.network1.Manager",
+                        "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;
@@ -2056,6 +2071,7 @@ static int help(void) {
                "  label                  Show current address label entries in the kernel\n"
                "  delete DEVICES...      Delete virtual netdevs\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"
@@ -2157,6 +2173,7 @@ static int networkctl_main(int argc, char *argv[]) {
                 { "label",       VERB_ANY, VERB_ANY, 0,            list_address_labels },
                 { "delete",      2,        VERB_ANY, 0,            link_delete         },
                 { "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         },
                 {}
index 88bafb4d20c00bf536bd2c0fe40b8d33258c6c41..1567bd7ee9112b7db9d5c4e4f73514cc3d80da93 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;
 
@@ -187,7 +205,7 @@ static int address_compare_func(const Address *a1, const Address *a2) {
         }
 }
 
-DEFINE_PRIVATE_HASH_OPS(address_hash_ops, Address, address_hash_func, address_compare_func);
+DEFINE_HASH_OPS(address_hash_ops, Address, address_hash_func, address_compare_func);
 
 bool address_equal(Address *a1, Address *a2) {
         if (a1 == a2)
@@ -1053,6 +1071,7 @@ int config_parse_address_scope(const char *unit,
                 }
         }
 
+        n->scope_set = true;
         n = NULL;
         return 0;
 }
@@ -1125,5 +1144,8 @@ int address_section_verify(Address *address) {
                                          address->section->filename, address->section->line);
         }
 
+        if (!address->scope_set && in_addr_is_localhost(address->family, &address->in_addr) > 0)
+                address->scope = RT_SCOPE_HOST;
+
         return 0;
 }
index 76a30a54bcde39f467e264dc0c6b795e03bea764..bd0485e0abee59f54e197a33d050398b13116f8c 100644 (file)
@@ -39,6 +39,7 @@ struct Address {
         union in_addr_union in_addr;
         union in_addr_union in_addr_peer;
 
+        bool scope_set:1;
         bool ip_masquerade_done:1;
         bool manage_temporary_address:1;
         bool home_address:1;
@@ -65,8 +66,12 @@ 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;
+
 CONFIG_PARSER_PROTOTYPE(config_parse_address);
 CONFIG_PARSER_PROTOTYPE(config_parse_broadcast);
 CONFIG_PARSER_PROTOTYPE(config_parse_label);
index bdd88659c5415f4c481ed457babdafb0e641d0e2..c703f94f3ab6d052e54bbabf62c41e3d13abbdd5 100644 (file)
@@ -9,6 +9,8 @@
 #include "networkd-manager.h"
 #include "string-util.h"
 
+#define CAN_TERMINATION_OHM_VALUE 120
+
 static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
@@ -101,7 +103,7 @@ static int link_set_can(Link *link) {
                 };
 
                 if (link->network->can_bitrate > UINT32_MAX) {
-                        log_link_error(link, "bitrate (%zu) too big.", link->network->can_bitrate);
+                        log_link_error(link, "bitrate (%" PRIu64 ") too big.", link->network->can_bitrate);
                         return -ERANGE;
                 }
 
@@ -152,6 +154,17 @@ static int link_set_can(Link *link) {
                         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 eef7788c49efbd88509a9f90df576929e8ce12da..350fea634c6741056dbd80267ba0ff941d4dbebc 100644 (file)
@@ -182,30 +182,3 @@ int config_parse_duid_rawdata(
         ret->raw_data_len = count;
         return 0;
 }
-
-int config_parse_ip_service_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) {
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-
-        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;
-}
index a615998f92d922946fe2f2ed075643bacba8a678..88a2c64031c22fcc5a497c3e380d2699695a4074 100644 (file)
@@ -15,4 +15,3 @@ const struct ConfigPerfItem* networkd_gperf_lookup(const char *key, GPERF_LEN_TY
 
 CONFIG_PARSER_PROTOTYPE(config_parse_duid_type);
 CONFIG_PARSER_PROTOTYPE(config_parse_duid_rawdata);
-CONFIG_PARSER_PROTOTYPE(config_parse_ip_service_type);
index 6465a8cfe9c77e7e30d6a84faa83baee1da34057..8664d8cdc0d43782def4aa85ac2f7060e7c7ab6e 100644 (file)
@@ -390,7 +390,7 @@ int config_parse_dhcp_send_option(
                 break;
         }
         case DHCP_OPTION_DATA_STRING:
-                sz = cunescape(p, 0, &q);
+                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);
index a6dbe2e596c3029a05f4154a44eb5dcf3ba5bc61..bee75a6930ea690479e9d06b5e4fed00d6ff5928 100644 (file)
@@ -45,7 +45,7 @@ static int link_push_uplink_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) {
         size_t n_addresses = 0, n_allocated = 0;
         unsigned i;
 
-        log_link_debug(link, "Copying DNS server information from %s", link->ifname);
+        log_link_debug(link, "Copying DNS server information from link");
 
         if (!link->network)
                 return 0;
@@ -99,7 +99,7 @@ static int link_push_uplink_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) {
         if (!link->network)
                 return 0;
 
-        log_link_debug(link, "Copying NTP server information from %s", link->ifname);
+        log_link_debug(link, "Copying NTP server information from link");
 
         STRV_FOREACH(a, link->network->ntp) {
                 union in_addr_union ia;
@@ -148,7 +148,7 @@ static int link_push_uplink_sip_to_dhcp_server(Link *link, sd_dhcp_server *s) {
         if (!link->network)
                 return 0;
 
-        log_link_debug(link, "Copying SIP server information from %s", link->ifname);
+        log_link_debug(link, "Copying SIP server information from link");
 
         STRV_FOREACH(a, link->network->sip) {
                 union in_addr_union ia;
index 8bdc28be81e8e539ac6b0b1aaf9d69b3b0fd2ff1..13e3e32f40e8c6add46c852127b8f29d9beb6e5a 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include <netinet/in.h>
+#include <netinet/ip.h>
 #include <linux/if.h>
 #include <linux/if_arp.h>
 
@@ -74,11 +75,11 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
                 /* It seems kernel does not support that the prefix route cannot be configured with
                  * route table. Let's once drop the config and reconfigure them later. */
 
-                log_link_message_debug_errno(link, m, r, "Could not set DHCPv4 route, retrying later: %m");
+                log_link_message_debug_errno(link, m, r, "Could not set DHCPv4 route, retrying later");
                 link->dhcp4_route_failed = true;
                 link->manager->dhcp4_prefix_root_cannot_set_table = true;
         } else if (r < 0 && r != -EEXIST) {
-                log_link_message_warning_errno(link, m, r, "Could not set DHCPv4 route: %m");
+                log_link_message_warning_errno(link, m, r, "Could not set DHCPv4 route");
                 link_enter_failed(link);
                 return 1;
         }
@@ -240,9 +241,6 @@ static int link_set_dhcp_routes(Link *link) {
         if (!link->network) /* link went down while we configured the IP addresses? */
                 return 0;
 
-        if (!link->network->dhcp_use_routes)
-                return 0;
-
         if (!link_has_carrier(link) && !link->network->configure_without_carrier)
                 /* During configuring addresses, the link lost its carrier. As networkd is dropping
                  * the addresses now, let's not configure the routes either. */
@@ -290,37 +288,39 @@ static int link_set_dhcp_routes(Link *link) {
                 }
         }
 
-        for (i = 0; i < n; i++) {
-                _cleanup_(route_freep) Route *route = NULL;
-
-                /* if the DHCP server returns both a Classless Static Routes option and a Static Routes option,
-                   the DHCP client MUST ignore the Static Routes option. */
-                if (classless_route &&
-                    sd_dhcp_route_get_option(static_routes[i]) != SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE)
-                        continue;
-
-                r = route_new(&route);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not allocate route: %m");
-
-                route->family = AF_INET;
-                route->protocol = RTPROT_DHCP;
-                assert_se(sd_dhcp_route_get_gateway(static_routes[i], &route->gw.in) >= 0);
-                assert_se(sd_dhcp_route_get_destination(static_routes[i], &route->dst.in) >= 0);
-                assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0);
-                route->priority = link->network->dhcp_route_metric;
-                route->table = table;
-                route->mtu = link->network->dhcp_route_mtu;
-                route->scope = route_scope_from_address(route, &address);
-                if (IN_SET(route->scope, RT_SCOPE_LINK, RT_SCOPE_UNIVERSE))
-                        route->prefsrc.in = address;
+        if (link->network->dhcp_use_routes) {
+                for (i = 0; i < n; i++) {
+                        _cleanup_(route_freep) Route *route = NULL;
 
-                if (set_contains(link->dhcp_routes, route))
-                        continue;
+                        /* if the DHCP server returns both a Classless Static Routes option and a Static Routes option,
+                           the DHCP client MUST ignore the Static Routes option. */
+                        if (classless_route &&
+                            sd_dhcp_route_get_option(static_routes[i]) != SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE)
+                                continue;
 
-                r = dhcp_route_configure(&route, link);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not set route: %m");
+                        r = route_new(&route);
+                        if (r < 0)
+                                return log_link_error_errno(link, r, "Could not allocate route: %m");
+
+                        route->family = AF_INET;
+                        route->protocol = RTPROT_DHCP;
+                        assert_se(sd_dhcp_route_get_gateway(static_routes[i], &route->gw.in) >= 0);
+                        assert_se(sd_dhcp_route_get_destination(static_routes[i], &route->dst.in) >= 0);
+                        assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0);
+                        route->priority = link->network->dhcp_route_metric;
+                        route->table = table;
+                        route->mtu = link->network->dhcp_route_mtu;
+                        route->scope = route_scope_from_address(route, &address);
+                        if (IN_SET(route->scope, RT_SCOPE_LINK, RT_SCOPE_UNIVERSE))
+                                route->prefsrc.in = address;
+
+                        if (set_contains(link->dhcp_routes, route))
+                                continue;
+
+                        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);
@@ -1704,6 +1704,33 @@ int config_parse_dhcp_request_options(
         return 0;
 }
 
+int config_parse_dhcp_ip_service_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) {
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        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;
+}
+
 static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
         [DHCP_CLIENT_ID_MAC] = "mac",
         [DHCP_CLIENT_ID_DUID] = "duid",
index fce11ef671e65783c454a9c6cb83639936678bdd..95fa5ee4b5bce81c8e42c306658cbc186b6ffee2 100644 (file)
@@ -27,3 +27,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_black_listed_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);
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 8f3b2e92f8fff739fb6ae14b2054bea2ed754b74..62f84faee3ac169541b8d29f885ed0eaff281aa3 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "alloc-util.h"
 #include "bus-common-errors.h"
+#include "bus-polkit.h"
 #include "bus-util.h"
 #include "dns-domain.h"
 #include "networkd-link-bus.h"
@@ -571,6 +572,35 @@ int bus_link_method_revert_dns(sd_bus_message *message, void *userdata, sd_bus_e
         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);
+}
+
 int bus_link_method_renew(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Link *l = userdata;
         int r;
@@ -620,6 +650,12 @@ int bus_link_method_reconfigure(sd_bus_message *message, void *userdata, sd_bus_
         if (r < 0)
                 return r;
 
+        link_set_state(l, LINK_STATE_INITIALIZED);
+        r = link_save(l);
+        if (r < 0)
+                return r;
+        link_clean(l);
+
         return sd_bus_reply_method_return(message, NULL);
 }
 
@@ -644,6 +680,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
index 09e4ad68a11606cec3262aeaf66281b6027c596f..afe0197dbd7aaa3c67af7f9722ef5cf668b5ebde 100644 (file)
@@ -31,4 +31,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 d9a695b3a41573d9990d070c8a87b9dfb4011b17..d7845760c5e0004ec9e93a17a12d71e524e31c40 100644 (file)
@@ -34,7 +34,6 @@
 #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 +41,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"
@@ -1116,7 +1116,7 @@ void link_check_ready(Link *link) {
         if (!link->routing_policy_rules_configured)
                 return;
 
-        if (!link->qdiscs_configured)
+        if (!link->tc_configured)
                 return;
 
         if (link_has_carrier(link) || !link->network->configure_without_carrier) {
@@ -1222,6 +1222,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);
@@ -1257,6 +1258,35 @@ static int link_request_set_addresses(Link *link) {
                         link->address_messages++;
         }
 
+        if (IN_SET(link->network->router_prefix_delegation,
+                   RADV_PREFIX_DELEGATION_STATIC,
+                   RADV_PREFIX_DELEGATION_BOTH))
+                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_link_error_errno(link, r, "Could not allocate address: %m");
+
+                        r = sd_radv_prefix_get_prefix(p->radv_prefix, &address->in_addr.in6, &address->prefixlen);
+                        if (r < 0)
+                                return r;
+
+                        r = generate_ipv6_eui_64_address(link, &address->in_addr.in6);
+                        if (r < 0)
+                                return r;
+
+                        address->family = AF_INET6;
+                        r = address_configure(address, link, address_handler, true);
+                        if (r < 0)
+                                return log_link_warning_errno(link, r, "Could not set addresses: %m");
+                        if (r > 0)
+                                link->address_messages++;
+                }
+
         LIST_FOREACH(labels, label, link->network->address_labels) {
                 r = address_label_configure(label, link, NULL, false);
                 if (r < 0)
@@ -1514,11 +1544,26 @@ static int link_acquire_ipv6_conf(Link *link) {
 
                 log_link_debug(link, "Starting IPv6 Router Advertisements");
 
+                r = radv_emit_dns(link);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Failed to configure DNS or Domains in IPv6 Router Advertisement: %m");
+
                 r = sd_radv_start(link->radv);
                 if (r < 0 && r != -EBUSY)
                         return log_link_warning_errno(link, r, "Could not start IPv6 Router Advertisement: %m");
         }
 
+        if (link_dhcp6_enabled(link) && link->network->dhcp6_without_ra) {
+                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, true);
+                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");
+        }
+
         (void) dhcp6_request_prefix_delegation(link);
 
         return 0;
@@ -1648,7 +1693,7 @@ 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 event if it exists, when stable_secret is unset,
+                /* 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
@@ -1875,7 +1920,7 @@ static int link_new_bound_by_list(Link *link) {
                 if (strv_isempty(carrier->network->bind_carrier))
                         continue;
 
-                if (strv_fnmatch(carrier->network->bind_carrier, link->ifname, 0)) {
+                if (strv_fnmatch(carrier->network->bind_carrier, link->ifname)) {
                         r = link_put_carrier(link, carrier, &link->bound_by_links);
                         if (r < 0)
                                 return r;
@@ -1917,7 +1962,7 @@ static int link_new_bound_to_list(Link *link) {
         m = link->manager;
 
         HASHMAP_FOREACH (carrier, m->links, i) {
-                if (strv_fnmatch(link->network->bind_carrier, carrier->ifname, 0)) {
+                if (strv_fnmatch(link->network->bind_carrier, carrier->ifname)) {
                         r = link_put_carrier(link, carrier, &link->bound_to_links);
                         if (r < 0)
                                 return r;
@@ -2698,24 +2743,24 @@ 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;
 }
@@ -2727,7 +2772,7 @@ 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;
 
@@ -3023,9 +3068,6 @@ static int link_reconfigure_internal(Link *link, sd_netlink_message *m, bool for
         Network *network;
         int r;
 
-        if (IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_LINGER))
-                return 0;
-
         if (m) {
                 _cleanup_strv_free_ char **s = NULL;
 
@@ -3040,7 +3082,7 @@ static int link_reconfigure_internal(Link *link, sd_netlink_message *m, bool for
                 strv_free_and_replace(link->alternative_names, s);
         }
 
-        r = network_get(link->manager, link->sd_device, link->ifname, link->alternative_names,
+        r = network_get(link->manager, link->iftype, link->sd_device, link->ifname, link->alternative_names,
                         &link->mac, &link->permanent_mac, link->wlan_iftype, link->ssid, &link->bssid, &network);
         if (r == -ENOENT) {
                 link_enter_unmanaged(link);
@@ -3089,6 +3131,7 @@ static int link_reconfigure_internal(Link *link, sd_netlink_message *m, bool for
                 return r;
 
         link_set_state(link, LINK_STATE_INITIALIZED);
+        link_dirty(link);
 
         /* link_configure_duid() returns 0 if it requests product UUID. In that case,
          * link_configure() is called later asynchronously. */
@@ -3127,6 +3170,9 @@ int link_reconfigure(Link *link, bool force) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
         int r;
 
+        if (IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_LINGER))
+                return 0;
+
         r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_GETLINK,
                                      link->ifindex);
         if (r < 0)
@@ -3172,7 +3218,7 @@ static int link_initialized_and_synced(Link *link) {
                 if (r < 0)
                         return r;
 
-                r = network_get(link->manager, link->sd_device, link->ifname, link->alternative_names,
+                r = network_get(link->manager, link->iftype, link->sd_device, link->ifname, link->alternative_names,
                                 &link->mac, &link->permanent_mac, link->wlan_iftype, link->ssid, &link->bssid, &network);
                 if (r == -ENOENT) {
                         link_enter_unmanaged(link);
@@ -3952,8 +3998,14 @@ int link_save(Link *link) {
                 fprintf(f, "REQUIRED_FOR_ONLINE=%s\n",
                         yes_no(link->network->required_for_online));
 
-                fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s\n",
-                        strempty(link_operstate_to_string(link->network->required_operstate_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);
@@ -4294,5 +4346,10 @@ int log_link_message_full_errno(Link *link, sd_netlink_message *m, int level, in
         const char *err_msg = NULL;
 
         (void) sd_netlink_message_read_string(m, NLMSGERR_ATTR_MSG, &err_msg);
-        return log_link_full(link, level, err, "%s: %s%s%m", msg, strempty(err_msg), err_msg ? " " : "");
+        return log_link_full(link, level, err,
+                             "%s: %s%s%s%m",
+                             msg,
+                             strempty(err_msg),
+                             err_msg && !endswith(err_msg, ".") ? "." : "",
+                             err_msg ? " " : "");
 }
index 59a510731130a7c648adb4d267f4b4d097c01547..3d07d882cda14a4d9cc6269a85aadbf142781657 100644 (file)
@@ -80,7 +80,7 @@ typedef struct Link {
         unsigned nexthop_messages;
         unsigned routing_policy_rule_messages;
         unsigned routing_policy_rule_remove_messages;
-        unsigned qdisc_messages;
+        unsigned tc_messages;
         unsigned enslaving;
 
         Set *addresses;
@@ -116,7 +116,7 @@ typedef struct Link {
         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 setting_mtu:1;
         bool setting_genmode:1;
         bool ipv6_mtu_set:1;
index 660c2847eb385824fb5cc1e7ca47877de807d480..e956c5ed9ab1525f55821670f5dbdd16064b72b7 100644 (file)
@@ -6,7 +6,7 @@
 
 #include "alloc-util.h"
 #include "bus-common-errors.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
 #include "networkd-link-bus.h"
 #include "networkd-link.h"
 #include "networkd-manager-bus.h"
@@ -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);
 }
@@ -249,6 +253,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 380f5c1c61f14613e113284964e9f29c931b092e..804f33d6cb4896c21247a79d982e3816cfb89fee 100644 (file)
@@ -11,6 +11,7 @@
 #include "sd-netlink.h"
 
 #include "alloc-util.h"
+#include "bus-polkit.h"
 #include "bus-util.h"
 #include "conf-parser.h"
 #include "def.h"
@@ -493,7 +494,8 @@ 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) || 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),
@@ -504,7 +506,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) {
@@ -961,6 +963,7 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi
         _cleanup_free_ char *from = NULL, *to = NULL;
         RoutingPolicyRule *rule = NULL;
         const char *iif = NULL, *oif = NULL;
+        uint32_t suppress_prefixlen;
         Manager *m = userdata;
         unsigned flags;
         uint16_t type;
@@ -1137,6 +1140,20 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi
                 return 0;
         }
 
+        r = sd_netlink_message_read(message, FRA_UID_RANGE, sizeof(tmp->uid_range), &tmp->uid_range);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get FRA_UID_RANGE attribute, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_netlink_message_read_u32(message, FRA_SUPPRESS_PREFIXLEN, &suppress_prefixlen);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get FRA_SUPPRESS_PREFIXLEN attribute, ignoring: %m");
+                return 0;
+        }
+        if (r >= 0)
+                tmp->suppress_prefixlen = (int) suppress_prefixlen;
+
         (void) routing_policy_rule_get(m, tmp, &rule);
 
         if (DEBUG_LOGGING) {
@@ -1731,6 +1748,7 @@ int manager_new(Manager **ret) {
 
         *m = (Manager) {
                 .speed_meter_interval_usec = SPEED_METER_DEFAULT_TIME_INTERVAL,
+                .manage_foreign_routes = true,
         };
 
         m->state_file = strdup("/run/systemd/netif/state");
index f2f309ffb05e93357ad28b5f08b5ad05a61ced62..6ee023353749f5d76a1df2e669a37926d7e59aa5 100644 (file)
@@ -30,6 +30,7 @@ struct Manager {
         bool enumerating:1;
         bool dirty:1;
         bool restarting:1;
+        bool manage_foreign_routes;
 
         Set *dirty_links;
 
index fb3d6f2a841ace03b792adfbf004c3042ae658f5..fe1de6387ee2c1969a5d18773e9a82016869aa34 100644 (file)
 #include "networkd-manager.h"
 #include "networkd-ndisc.h"
 #include "networkd-route.h"
+#include "string-util.h"
 #include "strv.h"
 
 #define NDISC_DNSSL_MAX 64U
 #define NDISC_RDNSS_MAX 64U
 #define NDISC_PREFIX_LFT_MIN 7200U
 
+#define DAD_CONFLICTS_IDGEN_RETRIES_RFC7217 3
+
+/* https://tools.ietf.org/html/rfc5453 */
+/* https://www.iana.org/assignments/ipv6-interface-ids/ipv6-interface-ids.xml */
+
+#define SUBNET_ROUTER_ANYCAST_ADDRESS_RFC4291               ((struct in6_addr) { .s6_addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } })
+#define SUBNET_ROUTER_ANYCAST_PREFIXLEN                     8
+#define RESERVED_IPV6_INTERFACE_IDENTIFIERS_ADDRESS_RFC4291 ((struct in6_addr) { .s6_addr = { 0x02, 0x00, 0x5E, 0xFF, 0xFE } })
+#define RESERVED_IPV6_INTERFACE_IDENTIFIERS_PREFIXLEN       5
+#define RESERVED_SUBNET_ANYCAST_ADDRESSES_RFC4291           ((struct in6_addr) { .s6_addr = { 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } })
+#define RESERVED_SUBNET_ANYCAST_PREFIXLEN                   7
+
+#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. */
+
+        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 *addr) {
+        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(link->ifname, strlen(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));
+
+        memcpy(addr->s6_addr, prefix->s6_addr, l);
+        memcpy((uint8_t *) &addr->s6_addr + l, &rid, 16 - l);
+
+        return 0;
+}
+
 static int ndisc_netlink_route_message_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
@@ -192,12 +254,103 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
         return 0;
 }
 
+static int ndisc_router_generate_addresses(Link *link, unsigned prefixlen, uint32_t lifetime_preferred, Address *address, Set **ret) {
+        _cleanup_set_free_free_ Set *addresses = NULL;
+        struct in6_addr addr;
+        IPv6Token *j;
+        Iterator i;
+        int r;
+
+        assert(link);
+        assert(address);
+        assert(ret);
+
+        addresses = set_new(&address_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;
+
+                if (j->address_generation_type == IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE
+                    && memcmp(&j->prefix, &addr, FAMILY_ADDRESS_SIZE(address->family)) == 0) {
+                        /* 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);
+                                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;
+                }
+
+                if (have_address) {
+                        new_address->prefixlen = prefixlen;
+                        new_address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
+                        new_address->cinfo.ifa_prefered = lifetime_preferred;
+
+                        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);
+                }
+        }
+
+        /* fall back to EUI-64 if no tokens provided addresses */
+        if (set_isempty(addresses)) {
+                _cleanup_(address_freep) Address *new_address = NULL;
+
+                r = address_new(&new_address);
+                if (r < 0)
+                        return log_oom();
+
+                *new_address = *address;
+
+                r = generate_ipv6_eui_64_address(link, &new_address->in_addr.in6);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Failed to generate EUI64 address: %m");
+
+                new_address->prefixlen = prefixlen;
+                new_address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
+                new_address->cinfo.ifa_prefered = lifetime_preferred;
+
+                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);
+        }
+
+        *ret = TAKE_PTR(addresses);
+
+        return 0;
+}
+
 static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
-        _cleanup_(address_freep) Address *address = NULL;
-        Address *existing_address;
         uint32_t lifetime_valid, lifetime_preferred, lifetime_remaining;
-        usec_t time_now;
+        _cleanup_set_free_free_ Set *addresses = NULL;
+        _cleanup_(address_freep) Address *address = NULL;
         unsigned prefixlen;
+        usec_t time_now;
+        Address *existing_address, *a;
+        Iterator i;
         int r;
 
         assert(link);
@@ -232,50 +385,39 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to get prefix address: %m");
 
-        if (in_addr_is_null(AF_INET6, (const union in_addr_union *) &link->network->ipv6_token) == 0)
-                memcpy(((char *)&address->in_addr.in6) + 8, ((char *)&link->network->ipv6_token) + 8, 8);
-        else {
-                /* see RFC4291 section 2.5.1 */
-                address->in_addr.in6.s6_addr[8]  = link->mac.ether_addr_octet[0];
-                address->in_addr.in6.s6_addr[8] ^= 1 << 1;
-                address->in_addr.in6.s6_addr[9]  = link->mac.ether_addr_octet[1];
-                address->in_addr.in6.s6_addr[10] = link->mac.ether_addr_octet[2];
-                address->in_addr.in6.s6_addr[11] = 0xff;
-                address->in_addr.in6.s6_addr[12] = 0xfe;
-                address->in_addr.in6.s6_addr[13] = link->mac.ether_addr_octet[3];
-                address->in_addr.in6.s6_addr[14] = link->mac.ether_addr_octet[4];
-                address->in_addr.in6.s6_addr[15] = link->mac.ether_addr_octet[5];
-        }
-        address->prefixlen = prefixlen;
-        address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
-        address->cinfo.ifa_prefered = lifetime_preferred;
-
-        /* see RFC4862 section 5.5.3.e */
-        r = address_get(link, address->family, &address->in_addr, address->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)
-                        address->cinfo.ifa_valid = lifetime_valid;
-                else if (lifetime_remaining <= NDISC_PREFIX_LFT_MIN)
-                        address->cinfo.ifa_valid = lifetime_remaining;
+        r = ndisc_router_generate_addresses(link, prefixlen, lifetime_preferred, address, &addresses);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to generate SLAAC addresses: %m");
+
+        SET_FOREACH(a, addresses, i) {
+                /* see RFC4862 section 5.5.3.e */
+                r = address_get(link, a->family, &a->in_addr, 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;
+                        else if (lifetime_remaining <= NDISC_PREFIX_LFT_MIN)
+                                a->cinfo.ifa_valid = lifetime_remaining;
+                        else
+                                a->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN;
+                } else if (lifetime_valid > 0)
+                        a->cinfo.ifa_valid = lifetime_valid;
                 else
-                        address->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN;
-        } else if (lifetime_valid > 0)
-                address->cinfo.ifa_valid = lifetime_valid;
-        else
-                return 0; /* see RFC4862 section 5.5.3.d */
+                        return 0; /* see RFC4862 section 5.5.3.d */
 
-        if (address->cinfo.ifa_valid == 0)
-                return 0;
+                if (a->cinfo.ifa_valid == 0)
+                        continue;
 
-        r = address_configure(address, 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;
+                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;
+                }
+
+                if (r > 0)
+                        link->ndisc_messages++;
         }
-        if (r > 0)
-                link->ndisc_messages++;
 
         return 0;
 }
@@ -652,7 +794,8 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to get RA flags: %m");
 
-        if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) {
+        if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER) &&
+            link->network->ipv6_accept_ra_start_dhcp6_client) {
                 /* (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)
@@ -755,6 +898,30 @@ void ndisc_flush(Link *link) {
         link->ndisc_dnssl = set_free_free(link->ndisc_dnssl);
 }
 
+int ipv6token_new(IPv6Token **ret) {
+        IPv6Token *p;
+
+        p = new(IPv6Token, 1);
+        if (!p)
+                return -ENOMEM;
+
+        *p = (IPv6Token) {
+                 .address_generation_type = IPV6_TOKEN_ADDRESS_GENERATION_NONE,
+        };
+
+        *ret = TAKE_PTR(p);
+
+        return 0;
+}
+
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+                ipv6_token_hash_ops,
+                void,
+                trivial_hash_func,
+                trivial_compare_func,
+                IPv6Token,
+                free);
+
 int config_parse_ndisc_black_listed_prefix(
                 const char *unit,
                 const char *filename,
@@ -826,3 +993,75 @@ int config_parse_ndisc_black_listed_prefix(
 
         return 0;
 }
+
+int config_parse_address_generation_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) {
+
+        _cleanup_free_ IPv6Token *token = NULL;
+        union in_addr_union buffer;
+        Network *network = data;
+        const char *p;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue)) {
+                network->ipv6_tokens = ordered_hashmap_free(network->ipv6_tokens);
+                return 0;
+        }
+
+        r = ipv6token_new(&token);
+        if (r < 0)
+                return log_oom();
+
+        if ((p = startswith(rvalue, "static:")))
+                token->address_generation_type = IPV6_TOKEN_ADDRESS_GENERATION_STATIC;
+        else if ((p = startswith(rvalue, "prefixstable:")))
+                token->address_generation_type = IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE;
+        else {
+                token->address_generation_type = IPV6_TOKEN_ADDRESS_GENERATION_STATIC;
+                p = rvalue;
+        }
+
+        r = in_addr_from_string(AF_INET6, p, &buffer);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, 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,
+                           "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);
+        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);
+
+        return 0;
+}
index dc0a44f523096dd5b075d358994f1a9b7ff95cf3..02c2f8afda5432a3f96c0ffed94b5a2bcfaaa7b8 100644 (file)
@@ -5,6 +5,16 @@
 #include "networkd-link.h"
 #include "time-util.h"
 
+typedef struct IPv6Token IPv6Token;
+
+typedef enum IPv6TokenAddressGeneration {
+        IPV6_TOKEN_ADDRESS_GENERATION_NONE,
+        IPV6_TOKEN_ADDRESS_GENERATION_STATIC,
+        IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE,
+        _IPV6_TOKEN_ADDRESS_GENERATION_MAX,
+        _IPV6_TOKEN_ADDRESS_GENERATION_INVALID = -1,
+} IPv6TokenAddressGeneration;
+
 typedef struct NDiscRDNSS {
         usec_t valid_until;
         struct in6_addr address;
@@ -15,6 +25,16 @@ typedef struct NDiscDNSSL {
         /* The domain name follows immediately. */
 } NDiscDNSSL;
 
+struct IPv6Token {
+        IPv6TokenAddressGeneration address_generation_type;
+
+        uint8_t dad_counter;
+        struct in6_addr prefix;
+};
+
+int ipv6token_new(IPv6Token **ret);
+DEFINE_TRIVIAL_CLEANUP_FUNC(IPv6Token *, freep);
+
 static inline char* NDISC_DNSSL_DOMAIN(const NDiscDNSSL *n) {
         return ((char*) n) + ALIGN(sizeof(NDiscDNSSL));
 }
@@ -24,3 +44,4 @@ void ndisc_vacuum(Link *link);
 void ndisc_flush(Link *link);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_black_listed_prefix);
+CONFIG_PARSER_PROTOTYPE(config_parse_address_generation_type);
index 67b3789ee6c1e0de88fac93d6d2cbe6521a496be..8bf80afddd296082d74ddb430fb6e6d984149374 100644 (file)
@@ -14,6 +14,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
 #include "networkd-ndisc.h"
 #include "networkd-network.h"
 #include "qdisc.h"
+#include "tclass.h"
 #include "vlan-util.h"
 %}
 struct ConfigPerfItem;
@@ -27,289 +28,340 @@ struct ConfigPerfItem;
 %struct-type
 %includes
 %%
-Match.MACAddress,                       config_parse_hwaddrs,                            0,                             offsetof(Network, match_mac)
-Match.PermanentMACAddress,              config_parse_hwaddrs,                            0,                             offsetof(Network, match_permanent_mac)
-Match.Path,                             config_parse_match_strv,                         0,                             offsetof(Network, match_path)
-Match.Driver,                           config_parse_match_strv,                         0,                             offsetof(Network, match_driver)
-Match.Type,                             config_parse_match_strv,                         0,                             offsetof(Network, match_type)
-Match.WLANInterfaceType,                config_parse_match_strv,                         0,                             offsetof(Network, match_wlan_iftype)
-Match.SSID,                             config_parse_match_strv,                         0,                             offsetof(Network, match_ssid)
-Match.BSSID,                            config_parse_hwaddrs,                            0,                             offsetof(Network, match_bssid)
-Match.Name,                             config_parse_match_ifnames,                      1,                             offsetof(Network, match_name)
-Match.Property,                         config_parse_match_property,                     0,                             offsetof(Network, match_property)
-Match.Host,                             config_parse_net_condition,                      CONDITION_HOST,                offsetof(Network, conditions)
-Match.Virtualization,                   config_parse_net_condition,                      CONDITION_VIRTUALIZATION,      offsetof(Network, conditions)
-Match.KernelCommandLine,                config_parse_net_condition,                      CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, conditions)
-Match.KernelVersion,                    config_parse_net_condition,                      CONDITION_KERNEL_VERSION,      offsetof(Network, conditions)
-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.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
-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)
-Network.VLAN,                           config_parse_stacked_netdev,                     NETDEV_KIND_VLAN,              offsetof(Network, stacked_netdev_names)
-Network.MACVLAN,                        config_parse_stacked_netdev,                     NETDEV_KIND_MACVLAN,           offsetof(Network, stacked_netdev_names)
-Network.MACVTAP,                        config_parse_stacked_netdev,                     NETDEV_KIND_MACVTAP,           offsetof(Network, stacked_netdev_names)
-Network.IPVLAN,                         config_parse_stacked_netdev,                     NETDEV_KIND_IPVLAN,            offsetof(Network, stacked_netdev_names)
-Network.IPVTAP,                         config_parse_stacked_netdev,                     NETDEV_KIND_IPVTAP,            offsetof(Network, stacked_netdev_names)
-Network.VXLAN,                          config_parse_stacked_netdev,                     NETDEV_KIND_VXLAN,             offsetof(Network, stacked_netdev_names)
-Network.L2TP,                           config_parse_stacked_netdev,                     NETDEV_KIND_L2TP,              offsetof(Network, stacked_netdev_names)
-Network.MACsec,                         config_parse_stacked_netdev,                     NETDEV_KIND_MACSEC,            offsetof(Network, stacked_netdev_names)
-Network.Tunnel,                         config_parse_stacked_netdev,                     _NETDEV_KIND_TUNNEL,           offsetof(Network, stacked_netdev_names)
-Network.Xfrm,                           config_parse_stacked_netdev,                     NETDEV_KIND_XFRM,              offsetof(Network, stacked_netdev_names)
-Network.VRF,                            config_parse_ifname,                             0,                             offsetof(Network, vrf_name)
-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.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_ipv6token,                          0,                             offsetof(Network, ipv6_token)
-Network.LLDP,                           config_parse_lldp_mode,                          0,                             offsetof(Network, lldp_mode)
-Network.EmitLLDP,                       config_parse_lldp_emit,                          0,                             offsetof(Network, lldp_emit)
-Network.Address,                        config_parse_address,                            0,                             0
-Network.Gateway,                        config_parse_gateway,                            0,                             0
-Network.Domains,                        config_parse_domains,                            0,                             0
-Network.DNS,                            config_parse_dns,                                0,                             0
-Network.DNSDefaultRoute,                config_parse_tristate,                           0,                             offsetof(Network, dns_default_route)
-Network.LLMNR,                          config_parse_resolve_support,                    0,                             offsetof(Network, llmnr)
-Network.MulticastDNS,                   config_parse_resolve_support,                    0,                             offsetof(Network, mdns)
-Network.DNSOverTLS,                     config_parse_dns_over_tls_mode,                  0,                             offsetof(Network, dns_over_tls_mode)
-Network.DNSSEC,                         config_parse_dnssec_mode,                        0,                             offsetof(Network, dnssec_mode)
-Network.DNSSECNegativeTrustAnchors,     config_parse_dnssec_negative_trust_anchors,      0,                             0
-Network.NTP,                            config_parse_ntp,                                0,                             offsetof(Network, ntp)
-Network.IPForward,                      config_parse_address_family_with_kernel,         0,                             offsetof(Network, ip_forward)
-Network.IPMasquerade,                   config_parse_bool,                               0,                             offsetof(Network, ip_masquerade)
-Network.IPv6PrivacyExtensions,          config_parse_ipv6_privacy_extensions,            0,                             offsetof(Network, ipv6_privacy_extensions)
-Network.IPv6AcceptRA,                   config_parse_tristate,                           0,                             offsetof(Network, ipv6_accept_ra)
-Network.IPv6AcceptRouterAdvertisements, config_parse_tristate,                           0,                             offsetof(Network, ipv6_accept_ra)
-Network.IPv6DuplicateAddressDetection,  config_parse_int,                                0,                             offsetof(Network, ipv6_dad_transmits)
-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.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)
-Network.ProxyARP,                       config_parse_tristate,                           0,                             offsetof(Network, proxy_arp)
-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.KeepConfiguration,              config_parse_keep_configuration,                 0,                             offsetof(Network, keep_configuration)
-Address.Address,                        config_parse_address,                            0,                             0
-Address.Peer,                           config_parse_address,                            0,                             0
-Address.Broadcast,                      config_parse_broadcast,                          0,                             0
-Address.Label,                          config_parse_label,                              0,                             0
-Address.PreferredLifetime,              config_parse_lifetime,                           0,                             0
-Address.HomeAddress,                    config_parse_address_flags,                      0,                             0
-Address.ManageTemporaryAddress,         config_parse_address_flags,                      0,                             0
-Address.PrefixRoute,                    config_parse_address_flags,                      0,                             0 /* deprecated */
-Address.AddPrefixRoute,                 config_parse_address_flags,                      0,                             0
-Address.AutoJoin,                       config_parse_address_flags,                      0,                             0
-Address.DuplicateAddressDetection,      config_parse_duplicate_address_detection,        0,                             0
-Address.Scope,                          config_parse_address_scope,                      0,                             0
-IPv6AddressLabel.Prefix,                config_parse_address_label_prefix,               0,                             0
-IPv6AddressLabel.Label,                 config_parse_address_label,                      0,                             0
-Neighbor.Address,                       config_parse_neighbor_address,                   0,                             0
-Neighbor.LinkLayerAddress,              config_parse_neighbor_lladdr,                    0,                             0
-Neighbor.MACAddress,                    config_parse_neighbor_hwaddr,                    0,                             0 /* deprecated */
-RoutingPolicyRule.TypeOfService,        config_parse_routing_policy_rule_tos,            0,                             0
-RoutingPolicyRule.Priority,             config_parse_routing_policy_rule_priority,       0,                             0
-RoutingPolicyRule.Table,                config_parse_routing_policy_rule_table,          0,                             0
-RoutingPolicyRule.FirewallMark,         config_parse_routing_policy_rule_fwmark_mask,    0,                             0
-RoutingPolicyRule.From,                 config_parse_routing_policy_rule_prefix,         0,                             0
-RoutingPolicyRule.To,                   config_parse_routing_policy_rule_prefix,         0,                             0
-RoutingPolicyRule.IncomingInterface,    config_parse_routing_policy_rule_device,         0,                             0
-RoutingPolicyRule.OutgoingInterface,    config_parse_routing_policy_rule_device,         0,                             0
-RoutingPolicyRule.IPProtocol,           config_parse_routing_policy_rule_ip_protocol,    0,                             0
-RoutingPolicyRule.SourcePort,           config_parse_routing_policy_rule_port_range,     0,                             0
-RoutingPolicyRule.DestinationPort,      config_parse_routing_policy_rule_port_range,     0,                             0
-RoutingPolicyRule.InvertRule,           config_parse_routing_policy_rule_invert,         0,                             0
-RoutingPolicyRule.Family,               config_parse_routing_policy_rule_family,         0,                             0
-Route.Gateway,                          config_parse_gateway,                            0,                             0
-Route.Destination,                      config_parse_destination,                        0,                             0
-Route.Source,                           config_parse_destination,                        0,                             0
-Route.Metric,                           config_parse_route_priority,                     0,                             0
-Route.Scope,                            config_parse_route_scope,                        0,                             0
-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.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.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.RoutesToDNS,                     config_parse_bool,                               0,                             offsetof(Network, dhcp_routes_to_dns)
-DHCPv4.UseNTP,                          config_parse_bool,                               0,                             offsetof(Network, dhcp_use_ntp)
-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.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.MaxAttempts,                     config_parse_dhcp_max_attempts,                  0,                             0
-DHCPv4.UserClass,                       config_parse_dhcp_user_class,                    0,                             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_unsigned,                           0,                             offsetof(Network, dhcp_route_metric)
-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.IPServiceType,                   config_parse_ip_service_type,                    0,                             offsetof(Network, ip_service_type)
-DHCPv4.SendOption,                      config_parse_dhcp_send_option,                   0,                             offsetof(Network, dhcp_client_send_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)
-DHCPv6.RapidCommit,                     config_parse_bool,                               0,                             offsetof(Network, rapid_commit)
-DHCPv6.ForceDHCPv6PDOtherInformation,   config_parse_bool,                               0,                             offsetof(Network, dhcp6_force_pd_other_information)
-DHCPv6.PrefixDelegationHint,            config_parse_dhcp6_pd_hint,                      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.RouteTable,                config_parse_section_route_table,                0,                             0
-IPv6AcceptRA.BlackList,                 config_parse_ndisc_black_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.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.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)
-Bridge.HairPin,                         config_parse_tristate,                           0,                             offsetof(Network, hairpin)
-Bridge.FastLeave,                       config_parse_tristate,                           0,                             offsetof(Network, fast_leave)
-Bridge.AllowPortToBeRoot,               config_parse_tristate,                           0,                             offsetof(Network, allow_port_to_be_root)
-Bridge.UnicastFlood,                    config_parse_tristate,                           0,                             offsetof(Network, unicast_flood)
-Bridge.MulticastFlood,                  config_parse_tristate,                           0,                             offsetof(Network, multicast_flood)
-Bridge.MulticastToUnicast,              config_parse_tristate,                           0,                             offsetof(Network, multicast_to_unicast)
-Bridge.NeighborSuppression,             config_parse_tristate,                           0,                             offsetof(Network, neighbor_suppression)
-Bridge.Learning,                        config_parse_tristate,                           0,                             offsetof(Network, learning)
-Bridge.ProxyARP,                        config_parse_tristate,                           0,                             offsetof(Network, bridge_proxy_arp)
-Bridge.ProxyARPWiFi,                    config_parse_tristate,                           0,                             offsetof(Network, bridge_proxy_arp_wifi)
-Bridge.Priority,                        config_parse_bridge_port_priority,               0,                             offsetof(Network, priority)
-Bridge.MulticastRouter,                 config_parse_multicast_router,                   0,                             offsetof(Network, multicast_router)
-BridgeFDB.MACAddress,                   config_parse_fdb_hwaddr,                         0,                             0
-BridgeFDB.VLANId,                       config_parse_fdb_vlan_id,                        0,                             0
-BridgeFDB.Destination,                  config_parse_fdb_destination,                    0,                             0
-BridgeFDB.VNI,                          config_parse_fdb_vxlan_vni,                      0,                             0
-BridgeFDB.AssociatedWith,               config_parse_fdb_ntf_flags,                      0,                             0
-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
-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)
-IPv6PrefixDelegation.RouterPreference,  config_parse_router_preference,                  0,                             0
-IPv6PrefixDelegation.EmitDNS,           config_parse_bool,                               0,                             offsetof(Network, router_emit_dns)
-IPv6PrefixDelegation.DNS,               config_parse_radv_dns,                           0,                             0
-IPv6PrefixDelegation.EmitDomains,       config_parse_bool,                               0,                             offsetof(Network, router_emit_domains)
-IPv6PrefixDelegation.Domains,           config_parse_radv_search_domains,                0,                             0
-IPv6PrefixDelegation.DNSLifetimeSec,    config_parse_sec,                                0,                             offsetof(Network, router_dns_lifetime_usec)
-IPv6Prefix.Prefix,                      config_parse_prefix,                             0,                             0
-IPv6Prefix.OnLink,                      config_parse_prefix_flags,                       0,                             0
-IPv6Prefix.AddressAutoconfiguration,    config_parse_prefix_flags,                       0,                             0
-IPv6Prefix.ValidLifetimeSec,            config_parse_prefix_lifetime,                    0,                             0
-IPv6Prefix.PreferredLifetimeSec,        config_parse_prefix_lifetime,                    0,                             0
-IPv6RoutePrefix.Route,                  config_parse_route_prefix,                       0,                             0
-IPv6RoutePrefix.LifetimeSec,            config_parse_route_prefix_lifetime,              0,                             0
-CAN.BitRate,                            config_parse_si_size,                            0,                             offsetof(Network, can_bitrate)
-CAN.SamplePoint,                        config_parse_permille,                           0,                             offsetof(Network, can_sample_point)
-CAN.RestartSec,                         config_parse_sec,                                0,                             offsetof(Network, can_restart_us)
-CAN.TripleSampling,                     config_parse_tristate,                           0,                             offsetof(Network, can_triple_sampling)
-TrafficControlQueueingDiscipline.Parent,                                     config_parse_tc_qdiscs_parent,                               0, 0
-TrafficControlQueueingDiscipline.NetworkEmulatorDelaySec,                    config_parse_tc_network_emulator_delay,                      0, 0
-TrafficControlQueueingDiscipline.NetworkEmulatorDelayJitterSec,              config_parse_tc_network_emulator_delay,                      0, 0
-TrafficControlQueueingDiscipline.NetworkEmulatorLossRate,                    config_parse_tc_network_emulator_rate,                       0, 0
-TrafficControlQueueingDiscipline.NetworkEmulatorDuplicateRate,               config_parse_tc_network_emulator_rate,                       0, 0
-TrafficControlQueueingDiscipline.NetworkEmulatorPacketLimit,                 config_parse_tc_network_emulator_packet_limit,               0, 0
-TrafficControlQueueingDiscipline.TokenBufferFilterRate,                      config_parse_tc_token_buffer_filter_size,                    0, 0
-TrafficControlQueueingDiscipline.TokenBufferFilterBurst,                     config_parse_tc_token_buffer_filter_size,                    0, 0
-TrafficControlQueueingDiscipline.TokenBufferFilterLimitSize,                 config_parse_tc_token_buffer_filter_size,                    0, 0
-TrafficControlQueueingDiscipline.TokenBufferFilterMTUBytes,                  config_parse_tc_token_buffer_filter_size,                    0, 0
-TrafficControlQueueingDiscipline.TokenBufferFilterMPUBytes,                  config_parse_tc_token_buffer_filter_size,                    0, 0
-TrafficControlQueueingDiscipline.TokenBufferFilterPeakRate,                  config_parse_tc_token_buffer_filter_size,                    0, 0
-TrafficControlQueueingDiscipline.TokenBufferFilterLatencySec,                config_parse_tc_token_buffer_filter_latency,                 0, 0
-TrafficControlQueueingDiscipline.StochasticFairnessQueueingPerturbPeriodSec, config_parse_tc_stochastic_fairness_queueing_perturb_period, 0, 0
-TrafficControlQueueingDiscipline.FairQueuingControlledDelayPacketLimit,      config_parse_tc_fair_queuing_controlled_delay_u32,           0, 0
-TrafficControlQueueingDiscipline.FairQueuingControlledDelayMemoryLimit,      config_parse_tc_fair_queuing_controlled_delay_size,          0, 0
-TrafficControlQueueingDiscipline.FairQueuingControlledDelayFlows,            config_parse_tc_fair_queuing_controlled_delay_u32,           0, 0
-TrafficControlQueueingDiscipline.FairQueuingControlledDelayQuantum,          config_parse_tc_fair_queuing_controlled_delay_size,          0, 0
-TrafficControlQueueingDiscipline.FairQueuingControlledDelayTargetSec,        config_parse_tc_fair_queuing_controlled_delay_usec,          0, 0
-TrafficControlQueueingDiscipline.FairQueuingControlledDelayIntervalSec,      config_parse_tc_fair_queuing_controlled_delay_usec,          0, 0
-TrafficControlQueueingDiscipline.FairQueuingControlledDelayCEThresholdSec,   config_parse_tc_fair_queuing_controlled_delay_usec,          0, 0
-TrafficControlQueueingDiscipline.FairQueuingControlledDelayECN,              config_parse_tc_fair_queuing_controlled_delay_bool,          0, 0
-TrafficControlQueueingDiscipline.FairQueueTrafficPolicingPacketLimit,        config_parse_tc_fair_queue_traffic_policing_u32,             0, 0
-TrafficControlQueueingDiscipline.FairQueueTrafficPolicingFlowLimit,          config_parse_tc_fair_queue_traffic_policing_u32,             0, 0
-TrafficControlQueueingDiscipline.FairQueueTrafficPolicingQuantum,            config_parse_tc_fair_queue_traffic_policing_size,            0, 0
-TrafficControlQueueingDiscipline.FairQueueTrafficPolicingInitialQuantum,     config_parse_tc_fair_queue_traffic_policing_size,            0, 0
-TrafficControlQueueingDiscipline.FairQueueTrafficPolicingMaximumRate,        config_parse_tc_fair_queue_traffic_policing_max_rate,        0, 0
-TrafficControlQueueingDiscipline.FairQueueTrafficPolicingBuckets,            config_parse_tc_fair_queue_traffic_policing_u32,             0, 0
-TrafficControlQueueingDiscipline.FairQueueTrafficPolicingOrphanMask,         config_parse_tc_fair_queue_traffic_policing_u32,             0, 0
-TrafficControlQueueingDiscipline.FairQueueTrafficPolicingPacing,             config_parse_tc_fair_queue_traffic_policing_bool,            0, 0
-TrafficControlQueueingDiscipline.FairQueueTrafficPolicingCEThresholdSec,     config_parse_tc_fair_queue_traffic_policing_usec,            0, 0
-TrafficControlQueueingDiscipline.ControlledDelayPacketLimit,                 config_parse_tc_controlled_delay_u32,                        0, 0
-TrafficControlQueueingDiscipline.ControlledDelayTargetSec,                   config_parse_tc_controlled_delay_usec,                       0, 0
-TrafficControlQueueingDiscipline.ControlledDelayIntervalSec,                 config_parse_tc_controlled_delay_usec,                       0, 0
-TrafficControlQueueingDiscipline.ControlledDelayCEThresholdSec,              config_parse_tc_controlled_delay_usec,                       0, 0
-TrafficControlQueueingDiscipline.ControlledDelayECN,                         config_parse_tc_controlled_delay_bool,                       0, 0
+Match.MACAddress,                            config_parse_hwaddrs,                                     0,                             offsetof(Network, match_mac)
+Match.PermanentMACAddress,                   config_parse_hwaddrs,                                     0,                             offsetof(Network, match_permanent_mac)
+Match.Path,                                  config_parse_match_strv,                                  0,                             offsetof(Network, match_path)
+Match.Driver,                                config_parse_match_strv,                                  0,                             offsetof(Network, match_driver)
+Match.Type,                                  config_parse_match_strv,                                  0,                             offsetof(Network, match_type)
+Match.WLANInterfaceType,                     config_parse_match_strv,                                  0,                             offsetof(Network, match_wlan_iftype)
+Match.SSID,                                  config_parse_match_strv,                                  0,                             offsetof(Network, match_ssid)
+Match.BSSID,                                 config_parse_hwaddrs,                                     0,                             offsetof(Network, match_bssid)
+Match.Name,                                  config_parse_match_ifnames,                               1,                             offsetof(Network, match_name)
+Match.Property,                              config_parse_match_property,                              0,                             offsetof(Network, match_property)
+Match.Host,                                  config_parse_net_condition,                               CONDITION_HOST,                offsetof(Network, conditions)
+Match.Virtualization,                        config_parse_net_condition,                               CONDITION_VIRTUALIZATION,      offsetof(Network, conditions)
+Match.KernelCommandLine,                     config_parse_net_condition,                               CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, conditions)
+Match.KernelVersion,                         config_parse_net_condition,                               CONDITION_KERNEL_VERSION,      offsetof(Network, conditions)
+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.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
+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)
+Network.VLAN,                                config_parse_stacked_netdev,                              NETDEV_KIND_VLAN,              offsetof(Network, stacked_netdev_names)
+Network.MACVLAN,                             config_parse_stacked_netdev,                              NETDEV_KIND_MACVLAN,           offsetof(Network, stacked_netdev_names)
+Network.MACVTAP,                             config_parse_stacked_netdev,                              NETDEV_KIND_MACVTAP,           offsetof(Network, stacked_netdev_names)
+Network.IPVLAN,                              config_parse_stacked_netdev,                              NETDEV_KIND_IPVLAN,            offsetof(Network, stacked_netdev_names)
+Network.IPVTAP,                              config_parse_stacked_netdev,                              NETDEV_KIND_IPVTAP,            offsetof(Network, stacked_netdev_names)
+Network.VXLAN,                               config_parse_stacked_netdev,                              NETDEV_KIND_VXLAN,             offsetof(Network, stacked_netdev_names)
+Network.L2TP,                                config_parse_stacked_netdev,                              NETDEV_KIND_L2TP,              offsetof(Network, stacked_netdev_names)
+Network.MACsec,                              config_parse_stacked_netdev,                              NETDEV_KIND_MACSEC,            offsetof(Network, stacked_netdev_names)
+Network.Tunnel,                              config_parse_stacked_netdev,                              _NETDEV_KIND_TUNNEL,           offsetof(Network, stacked_netdev_names)
+Network.Xfrm,                                config_parse_stacked_netdev,                              NETDEV_KIND_XFRM,              offsetof(Network, stacked_netdev_names)
+Network.VRF,                                 config_parse_ifname,                                      0,                             offsetof(Network, vrf_name)
+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.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
+Network.LLDP,                                config_parse_lldp_mode,                                   0,                             offsetof(Network, lldp_mode)
+Network.EmitLLDP,                            config_parse_lldp_emit,                                   0,                             offsetof(Network, lldp_emit)
+Network.Address,                             config_parse_address,                                     0,                             0
+Network.Gateway,                             config_parse_gateway,                                     0,                             0
+Network.Domains,                             config_parse_domains,                                     0,                             0
+Network.DNS,                                 config_parse_dns,                                         0,                             0
+Network.DNSDefaultRoute,                     config_parse_tristate,                                    0,                             offsetof(Network, dns_default_route)
+Network.LLMNR,                               config_parse_resolve_support,                             0,                             offsetof(Network, llmnr)
+Network.MulticastDNS,                        config_parse_resolve_support,                             0,                             offsetof(Network, mdns)
+Network.DNSOverTLS,                          config_parse_dns_over_tls_mode,                           0,                             offsetof(Network, dns_over_tls_mode)
+Network.DNSSEC,                              config_parse_dnssec_mode,                                 0,                             offsetof(Network, dnssec_mode)
+Network.DNSSECNegativeTrustAnchors,          config_parse_dnssec_negative_trust_anchors,               0,                             0
+Network.NTP,                                 config_parse_ntp,                                         0,                             offsetof(Network, ntp)
+Network.IPForward,                           config_parse_address_family_with_kernel,                  0,                             offsetof(Network, ip_forward)
+Network.IPMasquerade,                        config_parse_bool,                                        0,                             offsetof(Network, ip_masquerade)
+Network.IPv6PrivacyExtensions,               config_parse_ipv6_privacy_extensions,                     0,                             offsetof(Network, ipv6_privacy_extensions)
+Network.IPv6AcceptRA,                        config_parse_tristate,                                    0,                             offsetof(Network, ipv6_accept_ra)
+Network.IPv6AcceptRouterAdvertisements,      config_parse_tristate,                                    0,                             offsetof(Network, ipv6_accept_ra)
+Network.IPv6DuplicateAddressDetection,       config_parse_int,                                         0,                             offsetof(Network, ipv6_dad_transmits)
+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.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)
+Network.ProxyARP,                            config_parse_tristate,                                    0,                             offsetof(Network, proxy_arp)
+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.KeepConfiguration,                   config_parse_keep_configuration,                          0,                             offsetof(Network, keep_configuration)
+Address.Address,                             config_parse_address,                                     0,                             0
+Address.Peer,                                config_parse_address,                                     0,                             0
+Address.Broadcast,                           config_parse_broadcast,                                   0,                             0
+Address.Label,                               config_parse_label,                                       0,                             0
+Address.PreferredLifetime,                   config_parse_lifetime,                                    0,                             0
+Address.HomeAddress,                         config_parse_address_flags,                               0,                             0
+Address.ManageTemporaryAddress,              config_parse_address_flags,                               0,                             0
+Address.PrefixRoute,                         config_parse_address_flags,                               0,                             0 /* deprecated */
+Address.AddPrefixRoute,                      config_parse_address_flags,                               0,                             0
+Address.AutoJoin,                            config_parse_address_flags,                               0,                             0
+Address.DuplicateAddressDetection,           config_parse_duplicate_address_detection,                 0,                             0
+Address.Scope,                               config_parse_address_scope,                               0,                             0
+IPv6AddressLabel.Prefix,                     config_parse_address_label_prefix,                        0,                             0
+IPv6AddressLabel.Label,                      config_parse_address_label,                               0,                             0
+Neighbor.Address,                            config_parse_neighbor_address,                            0,                             0
+Neighbor.LinkLayerAddress,                   config_parse_neighbor_lladdr,                             0,                             0
+Neighbor.MACAddress,                         config_parse_neighbor_hwaddr,                             0,                             0 /* deprecated */
+RoutingPolicyRule.TypeOfService,             config_parse_routing_policy_rule_tos,                     0,                             0
+RoutingPolicyRule.Priority,                  config_parse_routing_policy_rule_priority,                0,                             0
+RoutingPolicyRule.Table,                     config_parse_routing_policy_rule_table,                   0,                             0
+RoutingPolicyRule.FirewallMark,              config_parse_routing_policy_rule_fwmark_mask,             0,                             0
+RoutingPolicyRule.From,                      config_parse_routing_policy_rule_prefix,                  0,                             0
+RoutingPolicyRule.To,                        config_parse_routing_policy_rule_prefix,                  0,                             0
+RoutingPolicyRule.IncomingInterface,         config_parse_routing_policy_rule_device,                  0,                             0
+RoutingPolicyRule.OutgoingInterface,         config_parse_routing_policy_rule_device,                  0,                             0
+RoutingPolicyRule.IPProtocol,                config_parse_routing_policy_rule_ip_protocol,             0,                             0
+RoutingPolicyRule.SourcePort,                config_parse_routing_policy_rule_port_range,              0,                             0
+RoutingPolicyRule.DestinationPort,           config_parse_routing_policy_rule_port_range,              0,                             0
+RoutingPolicyRule.InvertRule,                config_parse_routing_policy_rule_invert,                  0,                             0
+RoutingPolicyRule.Family,                    config_parse_routing_policy_rule_family,                  0,                             0
+RoutingPolicyRule.User,                      config_parse_routing_policy_rule_uid_range,               0,                             0
+RoutingPolicyRule.SuppressPrefixLength,      config_parse_routing_policy_rule_suppress_prefixlen,      0,                             0
+Route.Gateway,                               config_parse_gateway,                                     0,                             0
+Route.Destination,                           config_parse_destination,                                 0,                             0
+Route.Source,                                config_parse_destination,                                 0,                             0
+Route.Metric,                                config_parse_route_priority,                              0,                             0
+Route.Scope,                                 config_parse_route_scope,                                 0,                             0
+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.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.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.RoutesToDNS,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp_routes_to_dns)
+DHCPv4.UseNTP,                               config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_ntp)
+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.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.MaxAttempts,                          config_parse_dhcp_max_attempts,                           0,                             0
+DHCPv4.UserClass,                            config_parse_dhcp_user_class,                             0,                             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_unsigned,                                    0,                             offsetof(Network, dhcp_route_metric)
+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.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.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)
+DHCPv6.RapidCommit,                          config_parse_bool,                                        0,                             offsetof(Network, rapid_commit)
+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_bool,                                        0,                             offsetof(Network, dhcp6_without_ra)
+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_bool,                                        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
+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.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.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)
+Bridge.HairPin,                              config_parse_tristate,                                    0,                             offsetof(Network, hairpin)
+Bridge.FastLeave,                            config_parse_tristate,                                    0,                             offsetof(Network, fast_leave)
+Bridge.AllowPortToBeRoot,                    config_parse_tristate,                                    0,                             offsetof(Network, allow_port_to_be_root)
+Bridge.UnicastFlood,                         config_parse_tristate,                                    0,                             offsetof(Network, unicast_flood)
+Bridge.MulticastFlood,                       config_parse_tristate,                                    0,                             offsetof(Network, multicast_flood)
+Bridge.MulticastToUnicast,                   config_parse_tristate,                                    0,                             offsetof(Network, multicast_to_unicast)
+Bridge.NeighborSuppression,                  config_parse_tristate,                                    0,                             offsetof(Network, neighbor_suppression)
+Bridge.Learning,                             config_parse_tristate,                                    0,                             offsetof(Network, learning)
+Bridge.ProxyARP,                             config_parse_tristate,                                    0,                             offsetof(Network, bridge_proxy_arp)
+Bridge.ProxyARPWiFi,                         config_parse_tristate,                                    0,                             offsetof(Network, bridge_proxy_arp_wifi)
+Bridge.Priority,                             config_parse_bridge_port_priority,                        0,                             offsetof(Network, priority)
+Bridge.MulticastRouter,                      config_parse_multicast_router,                            0,                             offsetof(Network, multicast_router)
+BridgeFDB.MACAddress,                        config_parse_fdb_hwaddr,                                  0,                             0
+BridgeFDB.VLANId,                            config_parse_fdb_vlan_id,                                 0,                             0
+BridgeFDB.Destination,                       config_parse_fdb_destination,                             0,                             0
+BridgeFDB.VNI,                               config_parse_fdb_vxlan_vni,                               0,                             0
+BridgeFDB.AssociatedWith,                    config_parse_fdb_ntf_flags,                               0,                             0
+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
+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)
+IPv6PrefixDelegation.RouterPreference,       config_parse_router_preference,                           0,                             0
+IPv6PrefixDelegation.EmitDNS,                config_parse_bool,                                        0,                             offsetof(Network, router_emit_dns)
+IPv6PrefixDelegation.DNS,                    config_parse_radv_dns,                                    0,                             0
+IPv6PrefixDelegation.EmitDomains,            config_parse_bool,                                        0,                             offsetof(Network, router_emit_domains)
+IPv6PrefixDelegation.Domains,                config_parse_radv_search_domains,                         0,                             0
+IPv6PrefixDelegation.DNSLifetimeSec,         config_parse_sec,                                         0,                             offsetof(Network, router_dns_lifetime_usec)
+IPv6Prefix.Prefix,                           config_parse_prefix,                                      0,                             0
+IPv6Prefix.OnLink,                           config_parse_prefix_flags,                                0,                             0
+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)
+CAN.SamplePoint,                             config_parse_permille,                                    0,                             offsetof(Network, can_sample_point)
+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)
+QDisc.Parent,                                config_parse_qdisc_parent,                                _QDISC_KIND_INVALID,           0
+QDisc.Handle,                                config_parse_qdisc_handle,                                _QDISC_KIND_INVALID,           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.Overhead,                               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
+ControlledDelay.TargetSec,                   config_parse_controlled_delay_usec,                       QDISC_KIND_CODEL,              0
+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
+PFIFO.Parent,                                config_parse_qdisc_parent,                                QDISC_KIND_PFIFO,              0
+PFIFO.Handle,                                config_parse_qdisc_handle,                                QDISC_KIND_PFIFO,              0
+PFIFO.PacketLimit,                           config_parse_fifo_size,                                   QDISC_KIND_PFIFO,              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.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
+FairQueueing.Pacing,                         config_parse_fair_queueing_bool,                          QDISC_KIND_FQ,                 0
+FairQueueing.CEThresholdSec,                 config_parse_fair_queueing_usec,                          QDISC_KIND_FQ,                 0
+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.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.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
+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
+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_u32,                  TCLASS_KIND_HTB,               0
+HierarchyTokenBucketClass.Rate,              config_parse_hierarchy_token_bucket_rate,                 TCLASS_KIND_HTB,               0
+HierarchyTokenBucketClass.CeilRate,          config_parse_hierarchy_token_bucket_rate,                 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
+NetworkEmulator.DelayJitterSec,              config_parse_network_emulator_delay,                      QDISC_KIND_NETEM,              0
+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
+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.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.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)
-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
-DHCP.UseMTU,                            config_parse_bool,                               0,                             offsetof(Network, dhcp_use_mtu)
-DHCP.UseHostname,                       config_parse_bool,                               0,                             offsetof(Network, dhcp_use_hostname)
-DHCP.UseDomains,                        config_parse_dhcp_use_domains,                   0,                             offsetof(Network, dhcp_use_domains)
-DHCP.UseDomainName,                     config_parse_dhcp_use_domains,                   0,                             offsetof(Network, dhcp_use_domains)
-DHCP.UseRoutes,                         config_parse_bool,                               0,                             offsetof(Network, dhcp_use_routes)
-DHCP.Anonymize,                         config_parse_bool,                               0,                             offsetof(Network, dhcp_anonymize)
-DHCP.SendHostname,                      config_parse_bool,                               0,                             offsetof(Network, dhcp_send_hostname)
-DHCP.Hostname,                          config_parse_hostname,                           0,                             offsetof(Network, dhcp_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.DUIDType,                          config_parse_duid_type,                          0,                             offsetof(Network, duid)
-DHCP.DUIDRawData,                       config_parse_duid_rawdata,                       0,                             offsetof(Network, duid)
-DHCP.RouteMetric,                       config_parse_unsigned,                           0,                             offsetof(Network, dhcp_route_metric)
-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
-DHCP.ListenPort,                        config_parse_uint16,                             0,                             offsetof(Network, dhcp_client_port)
-DHCP.RapidCommit,                       config_parse_bool,                               0,                             offsetof(Network, rapid_commit)
-DHCP.ForceDHCPv6PDOtherInformation,     config_parse_bool,                               0,                             offsetof(Network, dhcp6_force_pd_other_information)
-DHCPv4.UseDomainName,                   config_parse_dhcp_use_domains,                   0,                             offsetof(Network, dhcp_use_domains)
-DHCPv4.CriticalConnection,              config_parse_tristate,                           0,                             offsetof(Network, dhcp_critical)
+Network.IPv4LL,                              config_parse_ipv4ll,                                      0,                             offsetof(Network, link_local)
+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
+DHCP.UseMTU,                                 config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_mtu)
+DHCP.UseHostname,                            config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_hostname)
+DHCP.UseDomains,                             config_parse_dhcp_use_domains,                            0,                             offsetof(Network, dhcp_use_domains)
+DHCP.UseDomainName,                          config_parse_dhcp_use_domains,                            0,                             offsetof(Network, dhcp_use_domains)
+DHCP.UseRoutes,                              config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_routes)
+DHCP.Anonymize,                              config_parse_bool,                                        0,                             offsetof(Network, dhcp_anonymize)
+DHCP.SendHostname,                           config_parse_bool,                                        0,                             offsetof(Network, dhcp_send_hostname)
+DHCP.Hostname,                               config_parse_hostname,                                    0,                             offsetof(Network, dhcp_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.DUIDType,                               config_parse_duid_type,                                   0,                             offsetof(Network, duid)
+DHCP.DUIDRawData,                            config_parse_duid_rawdata,                                0,                             offsetof(Network, duid)
+DHCP.RouteMetric,                            config_parse_unsigned,                                    0,                             offsetof(Network, dhcp_route_metric)
+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
+DHCP.ListenPort,                             config_parse_uint16,                                      0,                             offsetof(Network, dhcp_client_port)
+DHCP.RapidCommit,                            config_parse_bool,                                        0,                             offsetof(Network, rapid_commit)
+DHCP.ForceDHCPv6PDOtherInformation,          config_parse_bool,                                        0,                             offsetof(Network, dhcp6_force_pd_other_information)
+DHCPv4.UseDomainName,                        config_parse_dhcp_use_domains,                            0,                             offsetof(Network, dhcp_use_domains)
+DHCPv4.CriticalConnection,                   config_parse_tristate,                                    0,                             offsetof(Network, dhcp_critical)
+TrafficControlQueueingDiscipline.Parent,                        config_parse_qdisc_parent,             _QDISC_KIND_INVALID,           0
+TrafficControlQueueingDiscipline.NetworkEmulatorDelaySec,       config_parse_network_emulator_delay,   0,                             0
+TrafficControlQueueingDiscipline.NetworkEmulatorDelayJitterSec, config_parse_network_emulator_delay,   0,                             0
+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
index 4fd48be52a023720088fa2312a39cb2e7731ca00..40e4009bec2334f2245e7cb75ac92427d3449e70 100644 (file)
@@ -22,6 +22,7 @@
 #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. */
@@ -154,7 +155,7 @@ int network_verify(Network *network) {
         Prefix *prefix, *prefix_next;
         Route *route, *route_next;
         FdbEntry *fdb, *fdb_next;
-        QDisc *qdisc;
+        TrafficControl *tc;
         Iterator i;
 
         assert(network);
@@ -164,10 +165,10 @@ int network_verify(Network *network) {
             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)
-                log_warning("%s: No valid settings found in the [Match] section. "
-                            "The file will match all interfaces. "
-                            "If that is intended, please add Name=* in the [Match] section.",
-                            network->filename);
+                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))
@@ -316,9 +317,9 @@ 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);
 
         return 0;
 }
@@ -375,7 +376,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                 .n_ref = 1,
 
                 .required_for_online = true,
-                .required_operstate_for_online = LINK_OPERSTATE_DEGRADED,
+                .required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT,
                 .dhcp = ADDRESS_FAMILY_NO,
                 .dhcp_critical = -1,
                 .dhcp_use_ntp = true,
@@ -451,10 +452,12 @@ 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,
 
                 .keep_configuration = _KEEP_CONFIGURATION_INVALID,
 
                 .can_triple_sampling = -1,
+                .can_termination = -1,
                 .ip_service_type = -1,
         };
 
@@ -481,7 +484,21 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                               "IPv6Prefix\0"
                               "IPv6RoutePrefix\0"
                               "TrafficControlQueueingDiscipline\0"
-                              "CAN\0",
+                              "CAN\0"
+                              "QDisc\0"
+                              "CAKE\0"
+                              "ControlledDelay\0"
+                              "PFIFO\0"
+                              "FairQueueing\0"
+                              "FairQueueingControlledDelay\0"
+                              "GenericRandomEarlyDetection\0"
+                              "HierarchyTokenBucket\0"
+                              "HierarchyTokenBucketClass\0"
+                              "NetworkEmulator\0"
+                              "StochasticFairBlue\0"
+                              "StochasticFairnessQueueing\0"
+                              "TokenBucketFilter\0"
+                              "TrivialLinkEqualizer\0",
                               config_item_perf_lookup, network_network_gperf_lookup,
                               CONFIG_PARSE_WARN, network);
         if (r < 0)
@@ -682,7 +699,7 @@ 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->tc_by_section, traffic_control_free);
 
         if (network->manager &&
             network->manager->duids_requesting_uuid)
@@ -699,6 +716,7 @@ static Network *network_free(Network *network) {
 
         ordered_hashmap_free(network->dhcp_client_send_options);
         ordered_hashmap_free(network->dhcp_server_send_options);
+        ordered_hashmap_free(network->ipv6_tokens);
 
         return mfree(network);
 }
@@ -721,7 +739,7 @@ int network_get_by_name(Manager *manager, const char *name, Network **ret) {
         return 0;
 }
 
-int network_get(Manager *manager, sd_device *device,
+int network_get(Manager *manager, unsigned short iftype, sd_device *device,
                 const char *ifname, char * const *alternative_names,
                 const struct ether_addr *address, const struct ether_addr *permanent_address,
                 enum nl80211_iftype wlan_iftype, const char *ssid, const struct ether_addr *bssid,
@@ -737,7 +755,7 @@ int network_get(Manager *manager, sd_device *device,
                                      network->match_path, network->match_driver,
                                      network->match_type, network->match_name, network->match_property,
                                      network->match_wlan_iftype, network->match_ssid, network->match_bssid,
-                                     device, address, permanent_address,
+                                     iftype, device, address, permanent_address,
                                      ifname, alternative_names, wlan_iftype, ssid, bssid)) {
                         if (network->match_name && device) {
                                 const char *attr;
@@ -1300,18 +1318,18 @@ int config_parse_required_for_online(
                 void *userdata) {
 
         Network *network = data;
-        LinkOperationalState s;
+        LinkOperationalStateRange range;
         bool required = true;
         int r;
 
         if (isempty(rvalue)) {
                 network->required_for_online = true;
-                network->required_operstate_for_online = LINK_OPERSTATE_DEGRADED;
+                network->required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT;
                 return 0;
         }
 
-        s = link_operstate_from_string(rvalue);
-        if (s < 0) {
+        r = parse_operational_state_range(rvalue, &range);
+        if (r < 0) {
                 r = parse_boolean(rvalue);
                 if (r < 0) {
                         log_syntax(unit, LOG_ERR, filename, line, r,
@@ -1321,11 +1339,11 @@ int config_parse_required_for_online(
                 }
 
                 required = r;
-                s = LINK_OPERSTATE_DEGRADED;
+                range = LINK_OPERSTATE_RANGE_DEFAULT;
         }
 
         network->required_for_online = required;
-        network->required_operstate_for_online = s;
+        network->required_operstate_for_online = range;
 
         return 0;
 }
index e1c1c17241df4ad937e5bff8be921af79ef6c0e2..f8ecb1f687ba9ad0e132a3107f05998a1dc0b0a1 100644 (file)
@@ -22,6 +22,7 @@
 #include "networkd-ipv6-proxy-ndp.h"
 #include "networkd-lldp-rx.h"
 #include "networkd-lldp-tx.h"
+#include "networkd-ndisc.h"
 #include "networkd-neighbor.h"
 #include "networkd-nexthop.h"
 #include "networkd-radv.h"
@@ -29,7 +30,6 @@
 #include "networkd-routing-policy-rule.h"
 #include "networkd-util.h"
 #include "ordered-set.h"
-#include "qdisc.h"
 #include "resolve-util.h"
 
 typedef enum IPv6PrivacyExtensions {
@@ -125,6 +125,7 @@ struct Network {
         /* DHCPv6 Client support*/
         bool dhcp6_use_dns;
         bool dhcp6_use_ntp;
+        bool dhcp6_without_ra;
         uint8_t dhcp6_pd_length;
         struct in6_addr dhcp6_pd_address;
 
@@ -194,10 +195,11 @@ struct Network {
         uint32_t br_untagged_bitmap[BRIDGE_VLAN_BITMAP_LEN];
 
         /* CAN support */
-        size_t can_bitrate;
+        uint64_t can_bitrate;
         unsigned can_sample_point;
         usec_t can_restart_us;
         int can_triple_sampling;
+        int can_termination;
 
         AddressFamily ip_forward;
         bool ip_masquerade;
@@ -212,14 +214,15 @@ struct Network {
         bool ipv6_accept_ra_use_dns;
         bool ipv6_accept_ra_use_autonomous_prefix;
         bool ipv6_accept_ra_use_onlink_prefix;
+        bool ipv6_accept_ra_start_dhcp6_client;
         bool active_slave;
         bool primary_slave;
         DHCPUseDomains ipv6_accept_ra_use_domains;
         uint32_t ipv6_accept_ra_route_table;
         bool ipv6_accept_ra_route_table_set;
         Set *ndisc_black_listed_prefix;
+        OrderedHashmap *ipv6_tokens;
 
-        union in_addr_union ipv6_token;
         IPv6PrivacyExtensions ipv6_privacy_extensions;
 
         struct ether_addr *mac;
@@ -237,7 +240,7 @@ struct Network {
         bool iaid_set;
 
         bool required_for_online; /* Is this network required to be considered online? */
-        LinkOperationalState required_operstate_for_online;
+        LinkOperationalStateRange required_operstate_for_online;
 
         LLDPMode lldp_mode; /* LLDP reception */
         LLDPEmit lldp_emit; /* LLDP transmission */
@@ -273,7 +276,7 @@ struct Network {
         Hashmap *prefixes_by_section;
         Hashmap *route_prefixes_by_section;
         Hashmap *rules_by_section;
-        OrderedHashmap *qdiscs_by_section;
+        OrderedHashmap *tc_by_section;
 
         /* All kinds of DNS configuration */
         struct in_addr_data *dns;
@@ -302,7 +305,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
 int network_verify(Network *network);
 
 int network_get_by_name(Manager *manager, const char *name, Network **ret);
-int network_get(Manager *manager, sd_device *device, const char *ifname, char * const *alternative_names,
+int network_get(Manager *manager, unsigned short iftype, sd_device *device, const char *ifname, char * const *alternative_names,
                 const struct ether_addr *mac, const struct ether_addr *permanent_mac,
                 enum nl80211_iftype wlan_iftype, const char *ssid,
                 const struct ether_addr *bssid, Network **ret);
index d48609f985ad7a9b1498440ab02b1d0e14744e79..d9267dd805ab0b50a11c3b18dba6a0eef8e511e3 100644 (file)
@@ -319,6 +319,46 @@ int config_parse_prefix_lifetime(const char *unit,
         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;
+
+        r = parse_boolean(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to parse %s=, ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        p->assign = r;
+        p = NULL;
+
+        return 0;
+}
+
 int config_parse_route_prefix(const char *unit,
                               const char *filename,
                               unsigned line,
@@ -443,20 +483,29 @@ static int radv_get_ip6dns(Network *network, struct in6_addr **dns,
 
 static int radv_set_dns(Link *link, Link *uplink) {
         _cleanup_free_ struct in6_addr *dns = NULL;
-        size_t n_dns;
         usec_t lifetime_usec;
+        size_t n_dns;
         int r;
 
         if (!link->network->router_emit_dns)
                 return 0;
 
         if (link->network->router_dns) {
-                dns = newdup(struct in6_addr, link->network->router_dns,
-                             link->network->n_router_dns);
+                struct in6_addr *p;
+
+                dns = new(struct in6_addr, link->network->n_router_dns);
                 if (!dns)
                         return -ENOMEM;
 
-                n_dns = link->network->n_router_dns;
+                p = dns;
+                for (size_t i = 0; i < link->network->n_router_dns; i++)
+                        if (IN6_IS_ADDR_UNSPECIFIED(&link->network->router_dns[i])) {
+                                if (!IN6_IS_ADDR_UNSPECIFIED(&link->ipv6ll_address))
+                                        *(p++) = link->ipv6ll_address;
+                        } else
+                                *(p++) = link->network->router_dns[i];
+
+                n_dns = p - dns;
                 lifetime_usec = link->network->router_dns_lifetime_usec;
 
                 goto set_dns;
@@ -620,7 +669,7 @@ int radv_configure(Link *link) {
 
         }
 
-        return radv_emit_dns(link);
+        return 0;
 }
 
 int config_parse_radv_dns(
@@ -658,19 +707,30 @@ int config_parse_radv_dns(
                 if (r == 0)
                         break;
 
-                if (in_addr_from_string(AF_INET6, w, &a) >= 0) {
-                        struct in6_addr *m;
+                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,
+                                           "Failed to parse DNS server address, ignoring: %s", w);
+                                continue;
+                        }
 
-                        m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr));
-                        if (!m)
-                                return log_oom();
+                        if (in_addr_is_null(AF_INET6, &a)) {
+                                log_syntax(unit, LOG_ERR, filename, line, 0,
+                                           "DNS server address is null, ignoring: %s", w);
+                                continue;
+                        }
+                }
 
-                        m[n->n_router_dns++] = a.in6;
-                        n->router_dns = m;
+                struct in6_addr *m;
+                m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr));
+                if (!m)
+                        return log_oom();
 
-                } else
-                        log_syntax(unit, LOG_ERR, filename, line, 0,
-                                   "Failed to parse DNS server address, ignoring: %s", w);
+                m[n->n_router_dns++] = a.in6;
+                n->router_dns = m;
         }
 
         return 0;
index 21b323e83e2b3574963c3f148bffec0a67ee22c2..8bf697aff0bc8538e8989c9d7f87f7ebbec4b93d 100644 (file)
@@ -28,6 +28,8 @@ struct Prefix {
 
         sd_radv_prefix *radv_prefix;
 
+        bool assign;
+
         LIST_FIELDS(Prefix, prefixes);
 };
 
@@ -59,6 +61,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 2df8b864e881e8da1c8b92bd4d407abf9a387b13..de532eee7aa128bf7ff840512914694cc3e222f6 100644 (file)
@@ -17,7 +17,7 @@
 #include "string-util.h"
 #include "strxcpyx.h"
 #include "sysctl-util.h"
-#include "util.h"
+#include "vrf.h"
 
 #define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U
 
@@ -1006,7 +1006,7 @@ int config_parse_gateway(
                 if (r < 0)
                         return r;
 
-                if (streq(rvalue, "dhcp")) {
+                if (streq(rvalue, "_dhcp")) {
                         n->gateway_from_dhcp = true;
                         TAKE_PTR(n);
                         return 0;
@@ -1700,6 +1700,11 @@ int route_section_verify(Route *route, Network *network) {
                                          route->section->filename, route->section->line);
         }
 
+        if (!route->table_set && network->vrf) {
+                route->table = VRF(network->vrf)->table;
+                route->table_set = true;
+        }
+
         if (!route->table_set && IN_SET(route->type, RTN_LOCAL, RTN_BROADCAST, RTN_ANYCAST, RTN_NAT))
                 route->table = RT_TABLE_LOCAL;
 
index e7d09c4ad94a0689efba763565580cc2e8a08135..641f8840845c66d76c8468df0d810f9d25f1808e 100644 (file)
@@ -7,6 +7,7 @@
 #include "alloc-util.h"
 #include "conf-parser.h"
 #include "fileio.h"
+#include "format-util.h"
 #include "ip-protocol-list.h"
 #include "networkd-routing-policy-rule.h"
 #include "netlink-util.h"
@@ -16,6 +17,7 @@
 #include "socket-util.h"
 #include "string-util.h"
 #include "strv.h"
+#include "user-util.h"
 
 int routing_policy_rule_new(RoutingPolicyRule **ret) {
         RoutingPolicyRule *rule;
@@ -26,6 +28,9 @@ int routing_policy_rule_new(RoutingPolicyRule **ret) {
 
         *rule = (RoutingPolicyRule) {
                 .table = RT_TABLE_MAIN,
+                .uid_range.start = UID_INVALID,
+                .uid_range.end = UID_INVALID,
+                .suppress_prefixlen = -1,
         };
 
         *ret = rule;
@@ -93,6 +98,8 @@ static int routing_policy_rule_copy(RoutingPolicyRule *dest, RoutingPolicyRule *
         dest->protocol = src->protocol;
         dest->sport = src->sport;
         dest->dport = src->dport;
+        dest->uid_range = src->uid_range;
+        dest->suppress_prefixlen = src->suppress_prefixlen;
 
         return 0;
 }
@@ -118,10 +125,12 @@ static void routing_policy_rule_hash_func(const RoutingPolicyRule *rule, struct
                 siphash24_compress(&rule->fwmask, sizeof(rule->fwmask), state);
                 siphash24_compress(&rule->priority, sizeof(rule->priority), state);
                 siphash24_compress(&rule->table, sizeof(rule->table), state);
+                siphash24_compress(&rule->suppress_prefixlen, sizeof(rule->suppress_prefixlen), state);
 
                 siphash24_compress(&rule->protocol, sizeof(rule->protocol), state);
                 siphash24_compress(&rule->sport, sizeof(rule->sport), state);
                 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);
@@ -186,6 +195,10 @@ static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const Ro
                 if (r != 0)
                         return r;
 
+                r = CMP(a->suppress_prefixlen, b->suppress_prefixlen);
+                if (r != 0)
+                        return r;
+
                 r = CMP(a->protocol, b->protocol);
                 if (r != 0)
                         return r;
@@ -198,6 +211,10 @@ static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const Ro
                 if (r != 0)
                         return r;
 
+                r = memcmp(&a->uid_range, &b->uid_range, sizeof(a->uid_range));
+                if (r != 0)
+                        return r;
+
                 r = strcmp_ptr(a->iif, b->iif);
                 if (r != 0)
                         return r;
@@ -554,12 +571,24 @@ int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, link_netl
                         return log_link_error_errno(link, r, "Could not append FRA_DPORT_RANGE attribute: %m");
         }
 
+        if (rule->uid_range.start != UID_INVALID && rule->uid_range.end != UID_INVALID) {
+                r = sd_netlink_message_append_data(m, FRA_UID_RANGE, &rule->uid_range, sizeof(rule->uid_range));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append FRA_UID_RANGE attribute: %m");
+        }
+
         if (rule->invert_rule) {
                 r = sd_rtnl_message_routing_policy_rule_set_flags(m, FIB_RULE_INVERT);
                 if (r < 0)
                         return log_link_error_errno(link, r, "Could not append FIB_RULE_INVERT attribute: %m");
         }
 
+        if (rule->suppress_prefixlen >= 0) {
+                r = sd_netlink_message_append_u32(m, FRA_SUPPRESS_PREFIXLEN, (uint32_t) rule->suppress_prefixlen);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append FRA_SUPPRESS_PREFIXLEN attribute: %m");
+        }
+
         rule->link = link;
 
         r = netlink_call_async(link->manager->rtnl, NULL, m,
@@ -1056,6 +1085,93 @@ int config_parse_routing_policy_rule_family(
         return 0;
 }
 
+int config_parse_routing_policy_rule_uid_range(
+                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_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
+        Network *network = userdata;
+        uid_t start, end;
+        int r;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = routing_policy_rule_new_static(network, filename, section_line, &n);
+        if (r < 0)
+                return r;
+
+        r = get_user_creds(&rvalue, &start, NULL, NULL, NULL, 0);
+        if (r >= 0)
+                end = start;
+        else {
+                r = parse_uid_range(rvalue, &start, &end);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r,
+                                   "Invalid uid or uid range '%s', ignoring: %m", rvalue);
+                        return 0;
+                }
+        }
+
+        n->uid_range.start = start;
+        n->uid_range.end = end;
+        n = NULL;
+
+        return 0;
+}
+
+int config_parse_routing_policy_rule_suppress_prefixlen(
+                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_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL;
+        Network *network = userdata;
+        int r;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = routing_policy_rule_new_static(network, filename, section_line, &n);
+        if (r < 0)
+                return r;
+
+        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);
+                return 0;
+        }
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule suppress_prefixlen, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        n = NULL;
+
+        return 0;
+}
+
 static int routing_policy_rule_read_full_file(const char *state_file, char **ret) {
         _cleanup_free_ char *s = NULL;
         size_t size;
@@ -1170,6 +1286,21 @@ int routing_policy_serialize_rules(Set *rules, FILE *f) {
                         space = true;
                 }
 
+                if (rule->uid_range.start != UID_INVALID && rule->uid_range.end != UID_INVALID) {
+                        assert_cc(sizeof(uid_t) == sizeof(uint32_t));
+                        fprintf(f, "%suidrange="UID_FMT"-"UID_FMT,
+                                space ? " " : "",
+                                rule->uid_range.start, rule->uid_range.end);
+                        space = true;
+                }
+
+                if (rule->suppress_prefixlen >= 0) {
+                        fprintf(f, "%ssuppress_prefixlen=%d",
+                                space ? " " : "",
+                                rule->suppress_prefixlen);
+                        space = true;
+                }
+
                 fprintf(f, "%stable=%"PRIu32 "\n",
                         space ? " " : "",
                         rule->table);
@@ -1269,14 +1400,12 @@ int routing_policy_load_rules(const char *state_file, Set **rules) {
                                         continue;
                                 }
                         } else if (streq(a, "fwmark")) {
-
                                 r = parse_fwmark_fwmask(b, &rule->fwmark, &rule->fwmask);
                                 if (r < 0) {
                                         log_error_errno(r, "Failed to parse RPDB rule firewall mark or mask, ignoring: %s", a);
                                         continue;
                                 }
                         } else if (streq(a, "iif")) {
-
                                 if (free_and_strdup(&rule->iif, b) < 0)
                                         return log_oom();
 
@@ -1291,26 +1420,44 @@ int routing_policy_load_rules(const char *state_file, Set **rules) {
                                         continue;
                                 }
                         } else if (streq(a, "sourceport")) {
-
                                 r = parse_ip_port_range(b, &low, &high);
                                 if (r < 0) {
-                                        log_error_errno(r, "Invalid routing policy rule source port range, ignoring assignment:'%s'", b);
+                                        log_error_errno(r, "Invalid routing policy rule source port range, ignoring assignment: '%s'", b);
                                         continue;
                                 }
 
                                 rule->sport.start = low;
                                 rule->sport.end = high;
-
                         } else if (streq(a, "destinationport")) {
-
                                 r = parse_ip_port_range(b, &low, &high);
                                 if (r < 0) {
-                                        log_error_errno(r, "Invalid routing policy rule destination port range, ignoring assignment:'%s'", b);
+                                        log_error_errno(r, "Invalid routing policy rule destination port range, ignoring assignment: '%s'", b);
                                         continue;
                                 }
 
                                 rule->dport.start = low;
                                 rule->dport.end = high;
+                        } else if (streq(a, "uidrange")) {
+                                uid_t lower, upper;
+
+                                r = parse_uid_range(b, &lower, &upper);
+                                if (r < 0) {
+                                        log_error_errno(r, "Invalid routing policy rule uid range, ignoring assignment: '%s'", b);
+                                        continue;
+                                }
+
+                                rule->uid_range.start = lower;
+                                rule->uid_range.end = upper;
+                        } else if (streq(a, "suppress_prefixlen")) {
+                                r = parse_ip_prefix_length(b, &rule->suppress_prefixlen);
+                                if (r == -ERANGE) {
+                                        log_error_errno(r, "Prefix length outside of valid range 0-128, ignoring: %s", b);
+                                        continue;
+                                }
+                                if (r < 0) {
+                                        log_error_errno(r, "Failed to parse RPDB rule suppress_prefixlen, ignoring: %s", b);
+                                        continue;
+                                }
                         }
                 }
 
index 6b8e3102275e48611f154224a66a872cf977e2c8..21ca0e8021e2cd81ce0d395ec2e085b155383101 100644 (file)
@@ -49,6 +49,9 @@ struct RoutingPolicyRule {
 
         struct fib_rule_port_range sport;
         struct fib_rule_port_range dport;
+        struct fib_rule_uid_range uid_range;
+
+        int suppress_prefixlen;
 
         LIST_FIELDS(RoutingPolicyRule, rules);
 };
@@ -79,3 +82,5 @@ CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_port_range);
 CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_ip_protocol);
 CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_invert);
 CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_family);
+CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_uid_range);
+CONFIG_PARSER_PROTOTYPE(config_parse_routing_policy_rule_suppress_prefixlen);
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..b499661
--- /dev/null
@@ -0,0 +1,159 @@
+/* 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)
+                return log_syntax(unit, LOG_ERR, filename, line, r,
+                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+
+        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_ERR, 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)
+                return log_syntax(unit, LOG_ERR, filename, line, r,
+                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+
+        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_ERR, filename, line, r,
+                           "Failed to parse 'Overhead=', ignoring assignment: %s",
+                           rvalue);
+                return 0;
+        }
+        if (v < -64 || v > 256) {
+                log_syntax(unit, LOG_ERR, filename, line, 0,
+                           "Invalid 'Overhead=', ignoring assignment: %s",
+                           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 24b5720c41c4876955b861be9d80a4f2c290c472..65c724da751ee05ce81ff2982b8099464271eb80 100644 (file)
@@ -74,7 +74,7 @@ static int controlled_delay_fill_message(Link *link, QDisc *qdisc, sd_netlink_me
         return 0;
 }
 
-int config_parse_tc_controlled_delay_u32(
+int config_parse_controlled_delay_u32(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -125,7 +125,7 @@ int config_parse_tc_controlled_delay_u32(
         return 0;
 }
 
-int config_parse_tc_controlled_delay_usec(
+int config_parse_controlled_delay_usec(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -157,17 +157,17 @@ int config_parse_tc_controlled_delay_usec(
 
         cd = CODEL(qdisc);
 
-        if (streq(lvalue, "ControlledDelayTargetSec"))
+        if (streq(lvalue, "TargetSec"))
                 p = &cd->target_usec;
-        else if (streq(lvalue, "ControlledDelayIntervalSec"))
+        else if (streq(lvalue, "IntervalSec"))
                 p = &cd->interval_usec;
-        else if (streq(lvalue, "ControlledDelayCEThresholdSec"))
+        else if (streq(lvalue, "CEThresholdSec"))
                 p = &cd->ce_threshold_usec;
         else
                 assert_not_reached("Invalid lvalue");
 
         if (isempty(rvalue)) {
-                if (streq(lvalue, "ControlledDelayCEThresholdSec"))
+                if (streq(lvalue, "CEThresholdSec"))
                         *p = USEC_INFINITY;
                 else
                         *p = 0;
@@ -189,7 +189,7 @@ int config_parse_tc_controlled_delay_usec(
         return 0;
 }
 
-int config_parse_tc_controlled_delay_bool(
+int config_parse_controlled_delay_bool(
                 const char *unit,
                 const char *filename,
                 unsigned line,
index 075423dec14f7ca5b8a2ed3cea245c901bfcbfa6..4023a7dee578b3b398e1b7521b5cffd1b0085146 100644 (file)
@@ -19,6 +19,6 @@ typedef struct ControlledDelay {
 DEFINE_QDISC_CAST(CODEL, ControlledDelay);
 extern const QDiscVTable codel_vtable;
 
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_controlled_delay_u32);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_controlled_delay_usec);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_controlled_delay_bool);
+CONFIG_PARSER_PROTOTYPE(config_parse_controlled_delay_u32);
+CONFIG_PARSER_PROTOTYPE(config_parse_controlled_delay_usec);
+CONFIG_PARSER_PROTOTYPE(config_parse_controlled_delay_bool);
diff --git a/src/network/tc/fifo.c b/src/network/tc/fifo.c
new file mode 100644 (file)
index 0000000..2d2a863
--- /dev/null
@@ -0,0 +1,87 @@
+/* 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);
+
+        fifo = PFIFO(qdisc);
+
+        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_fifo_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(QDISC_KIND_PFIFO, 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");
+
+        fifo = PFIFO(qdisc);
+
+        if (isempty(rvalue)) {
+                fifo->limit = 0;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = safe_atou32(rvalue, &fifo->limit);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        qdisc = NULL;
+        return 0;
+}
+
+const QDiscVTable pfifo_vtable = {
+        .object_size = sizeof(FirstInFirstOut),
+        .tca_kind = "pfifo",
+        .fill_message = fifo_fill_message,
+};
diff --git a/src/network/tc/fifo.h b/src/network/tc/fifo.h
new file mode 100644 (file)
index 0000000..0297656
--- /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 FirstInFirstOut {
+        QDisc meta;
+
+        uint32_t limit;
+} FirstInFirstOut;
+
+DEFINE_QDISC_CAST(PFIFO, FirstInFirstOut);
+extern const QDiscVTable pfifo_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_fifo_size);
index a64b77651cda733a116174757f557a214bab9212..6c7932c70f738e5a441456545157a47e210a38ab 100644 (file)
@@ -10,8 +10,8 @@
 #include "qdisc.h"
 #include "string-util.h"
 
-static int fair_queuing_controlled_delay_init(QDisc *qdisc) {
-        FairQueuingControlledDelay *fqcd;
+static int fair_queueing_controlled_delay_init(QDisc *qdisc) {
+        FairQueueingControlledDelay *fqcd;
 
         assert(qdisc);
 
@@ -24,8 +24,8 @@ static int fair_queuing_controlled_delay_init(QDisc *qdisc) {
         return 0;
 }
 
-static int fair_queuing_controlled_delay_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
-        FairQueuingControlledDelay *fqcd;
+static int fair_queueing_controlled_delay_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+        FairQueueingControlledDelay *fqcd;
         int r;
 
         assert(link);
@@ -93,7 +93,7 @@ static int fair_queuing_controlled_delay_fill_message(Link *link, QDisc *qdisc,
         return 0;
 }
 
-int config_parse_tc_fair_queuing_controlled_delay_u32(
+int config_parse_fair_queueing_controlled_delay_u32(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -106,7 +106,7 @@ int config_parse_tc_fair_queuing_controlled_delay_u32(
                 void *userdata) {
 
         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
-        FairQueuingControlledDelay *fqcd;
+        FairQueueingControlledDelay *fqcd;
         Network *network = data;
         uint32_t *p;
         int r;
@@ -125,9 +125,9 @@ int config_parse_tc_fair_queuing_controlled_delay_u32(
 
         fqcd = FQ_CODEL(qdisc);
 
-        if (streq(lvalue, "FairQueuingControlledDelayPacketLimit"))
+        if (streq(lvalue, "PacketLimit"))
                 p = &fqcd->packet_limit;
-        else if (streq(lvalue, "FairQueuingControlledDelayFlows"))
+        else if (streq(lvalue, "Flows"))
                 p = &fqcd->flows;
         else
                 assert_not_reached("Invalid lvalue.");
@@ -152,7 +152,7 @@ int config_parse_tc_fair_queuing_controlled_delay_u32(
         return 0;
 }
 
-int config_parse_tc_fair_queuing_controlled_delay_usec(
+int config_parse_fair_queueing_controlled_delay_usec(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -165,7 +165,7 @@ int config_parse_tc_fair_queuing_controlled_delay_usec(
                 void *userdata) {
 
         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
-        FairQueuingControlledDelay *fqcd;
+        FairQueueingControlledDelay *fqcd;
         Network *network = data;
         usec_t *p;
         int r;
@@ -184,17 +184,17 @@ int config_parse_tc_fair_queuing_controlled_delay_usec(
 
         fqcd = FQ_CODEL(qdisc);
 
-        if (streq(lvalue, "FairQueuingControlledDelayTargetSec"))
+        if (streq(lvalue, "TargetSec"))
                 p = &fqcd->target_usec;
-        else if (streq(lvalue, "FairQueuingControlledDelayIntervalSec"))
+        else if (streq(lvalue, "IntervalSec"))
                 p = &fqcd->interval_usec;
-        else if (streq(lvalue, "FairQueuingControlledDelayCEThresholdSec"))
+        else if (streq(lvalue, "CEThresholdSec"))
                 p = &fqcd->ce_threshold_usec;
         else
                 assert_not_reached("Invalid lvalue.");
 
         if (isempty(rvalue)) {
-                if (streq(lvalue, "FairQueuingControlledDelayCEThresholdSec"))
+                if (streq(lvalue, "CEThresholdSec"))
                         *p = USEC_INFINITY;
                 else
                         *p = 0;
@@ -216,7 +216,7 @@ int config_parse_tc_fair_queuing_controlled_delay_usec(
         return 0;
 }
 
-int config_parse_tc_fair_queuing_controlled_delay_bool(
+int config_parse_fair_queueing_controlled_delay_bool(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -229,7 +229,7 @@ int config_parse_tc_fair_queuing_controlled_delay_bool(
                 void *userdata) {
 
         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
-        FairQueuingControlledDelay *fqcd;
+        FairQueueingControlledDelay *fqcd;
         Network *network = data;
         int r;
 
@@ -268,7 +268,7 @@ int config_parse_tc_fair_queuing_controlled_delay_bool(
         return 0;
 }
 
-int config_parse_tc_fair_queuing_controlled_delay_size(
+int config_parse_fair_queueing_controlled_delay_size(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -281,7 +281,7 @@ int config_parse_tc_fair_queuing_controlled_delay_size(
                 void *userdata) {
 
         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
-        FairQueuingControlledDelay *fqcd;
+        FairQueueingControlledDelay *fqcd;
         Network *network = data;
         uint64_t sz;
         uint32_t *p;
@@ -301,15 +301,15 @@ int config_parse_tc_fair_queuing_controlled_delay_size(
 
         fqcd = FQ_CODEL(qdisc);
 
-        if (streq(lvalue, "FairQueuingControlledDelayMemoryLimit"))
+        if (streq(lvalue, "MemoryLimit"))
                 p = &fqcd->memory_limit;
-        else if (streq(lvalue, "FairQueuingControlledDelayQuantum"))
+        else if (streq(lvalue, "Quantum"))
                 p = &fqcd->quantum;
         else
                 assert_not_reached("Invalid lvalue.");
 
         if (isempty(rvalue)) {
-                if (streq(lvalue, "FairQueuingControlledMemoryLimit"))
+                if (streq(lvalue, "MemoryLimit"))
                         *p = UINT32_MAX;
                 else
                         *p = 0;
@@ -339,8 +339,8 @@ int config_parse_tc_fair_queuing_controlled_delay_size(
 }
 
 const QDiscVTable fq_codel_vtable = {
-        .object_size = sizeof(FairQueuingControlledDelay),
+        .object_size = sizeof(FairQueueingControlledDelay),
         .tca_kind = "fq_codel",
-        .init = fair_queuing_controlled_delay_init,
-        .fill_message = fair_queuing_controlled_delay_fill_message,
+        .init = fair_queueing_controlled_delay_init,
+        .fill_message = fair_queueing_controlled_delay_fill_message,
 };
index aa74216fa40d99d312c4f8dd6e01ff3c23d8594f..1a80963df6b27ff30423c659498b0fc9788f2c9f 100644 (file)
@@ -6,7 +6,7 @@
 #include "qdisc.h"
 #include "time-util.h"
 
-typedef struct FairQueuingControlledDelay {
+typedef struct FairQueueingControlledDelay {
         QDisc meta;
 
         uint32_t packet_limit;
@@ -17,12 +17,12 @@ typedef struct FairQueuingControlledDelay {
         usec_t interval_usec;
         usec_t ce_threshold_usec;
         int ecn;
-} FairQueuingControlledDelay;
+} FairQueueingControlledDelay;
 
-DEFINE_QDISC_CAST(FQ_CODEL, FairQueuingControlledDelay);
+DEFINE_QDISC_CAST(FQ_CODEL, FairQueueingControlledDelay);
 extern const QDiscVTable fq_codel_vtable;
 
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queuing_controlled_delay_u32);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queuing_controlled_delay_usec);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queuing_controlled_delay_bool);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queuing_controlled_delay_size);
+CONFIG_PARSER_PROTOTYPE(config_parse_fair_queueing_controlled_delay_u32);
+CONFIG_PARSER_PROTOTYPE(config_parse_fair_queueing_controlled_delay_usec);
+CONFIG_PARSER_PROTOTYPE(config_parse_fair_queueing_controlled_delay_bool);
+CONFIG_PARSER_PROTOTYPE(config_parse_fair_queueing_controlled_delay_size);
index 58a008f4c2e0fdf0d3b1909b070284104ab24a13..c7eeec230737c9721e8e386b3014b86ea6a0ab87 100644 (file)
@@ -11,8 +11,8 @@
 #include "string-util.h"
 #include "util.h"
 
-static int fair_queue_traffic_policing_init(QDisc *qdisc) {
-        FairQueueTrafficPolicing *fq;
+static int fair_queueing_init(QDisc *qdisc) {
+        FairQueueing *fq;
 
         assert(qdisc);
 
@@ -24,8 +24,8 @@ static int fair_queue_traffic_policing_init(QDisc *qdisc) {
         return 0;
 }
 
-static int fair_queue_traffic_policing_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
-        FairQueueTrafficPolicing *fq;
+static int fair_queueing_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+        FairQueueing *fq;
         int r;
 
         assert(link);
@@ -102,7 +102,7 @@ static int fair_queue_traffic_policing_fill_message(Link *link, QDisc *qdisc, sd
         return 0;
 }
 
-int config_parse_tc_fair_queue_traffic_policing_u32(
+int config_parse_fair_queueing_u32(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -115,7 +115,7 @@ int config_parse_tc_fair_queue_traffic_policing_u32(
                 void *userdata) {
 
         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
-        FairQueueTrafficPolicing *fq;
+        FairQueueing *fq;
         Network *network = data;
         uint32_t *p;
         int r;
@@ -134,13 +134,13 @@ int config_parse_tc_fair_queue_traffic_policing_u32(
 
         fq = FQ(qdisc);
 
-        if (streq(lvalue, "FairQueueTrafficPolicingPacketLimit"))
+        if (streq(lvalue, "PacketLimit"))
                 p = &fq->packet_limit;
-        else if (streq(lvalue, "FairQueueTrafficPolicingFlowLimit"))
+        else if (streq(lvalue, "FlowLimit"))
                 p = &fq->flow_limit;
-        else if (streq(lvalue, "FairQueueTrafficPolicingBuckets"))
+        else if (streq(lvalue, "Buckets"))
                 p = &fq->buckets;
-        else if (streq(lvalue, "FairQueueTrafficPolicingOrphanMask"))
+        else if (streq(lvalue, "OrphanMask"))
                 p = &fq->orphan_mask;
         else
                 assert_not_reached("Invalid lvalue");
@@ -165,7 +165,7 @@ int config_parse_tc_fair_queue_traffic_policing_u32(
         return 0;
 }
 
-int config_parse_tc_fair_queue_traffic_policing_size(
+int config_parse_fair_queueing_size(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -178,7 +178,7 @@ int config_parse_tc_fair_queue_traffic_policing_size(
                 void *userdata) {
 
         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
-        FairQueueTrafficPolicing *fq;
+        FairQueueing *fq;
         Network *network = data;
         uint64_t sz;
         uint32_t *p;
@@ -198,9 +198,9 @@ int config_parse_tc_fair_queue_traffic_policing_size(
 
         fq = FQ(qdisc);
 
-        if (streq(lvalue, "FairQueueTrafficPolicingQuantum"))
+        if (streq(lvalue, "Quantum"))
                 p = &fq->quantum;
-        else if (streq(lvalue, "FairQueueTrafficPolicingInitialQuantum"))
+        else if (streq(lvalue, "InitialQuantum"))
                 p = &fq->initial_quantum;
         else
                 assert_not_reached("Invalid lvalue");
@@ -232,7 +232,7 @@ int config_parse_tc_fair_queue_traffic_policing_size(
         return 0;
 }
 
-int config_parse_tc_fair_queue_traffic_policing_bool(
+int config_parse_fair_queueing_bool(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -245,7 +245,7 @@ int config_parse_tc_fair_queue_traffic_policing_bool(
                 void *userdata) {
 
         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
-        FairQueueTrafficPolicing *fq;
+        FairQueueing *fq;
         Network *network = data;
         int r;
 
@@ -284,7 +284,7 @@ int config_parse_tc_fair_queue_traffic_policing_bool(
         return 0;
 }
 
-int config_parse_tc_fair_queue_traffic_policing_usec(
+int config_parse_fair_queueing_usec(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -297,7 +297,7 @@ int config_parse_tc_fair_queue_traffic_policing_usec(
                 void *userdata) {
 
         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
-        FairQueueTrafficPolicing *fq;
+        FairQueueing *fq;
         Network *network = data;
         usec_t sec;
         int r;
@@ -343,7 +343,7 @@ int config_parse_tc_fair_queue_traffic_policing_usec(
         return 0;
 }
 
-int config_parse_tc_fair_queue_traffic_policing_max_rate(
+int config_parse_fair_queueing_max_rate(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -356,7 +356,7 @@ int config_parse_tc_fair_queue_traffic_policing_max_rate(
                 void *userdata) {
 
         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
-        FairQueueTrafficPolicing *fq;
+        FairQueueing *fq;
         Network *network = data;
         uint64_t sz;
         int r;
@@ -403,8 +403,8 @@ int config_parse_tc_fair_queue_traffic_policing_max_rate(
 }
 
 const QDiscVTable fq_vtable = {
-        .init = fair_queue_traffic_policing_init,
-        .object_size = sizeof(FairQueueTrafficPolicing),
+        .init = fair_queueing_init,
+        .object_size = sizeof(FairQueueing),
         .tca_kind = "fq",
-        .fill_message = fair_queue_traffic_policing_fill_message,
+        .fill_message = fair_queueing_fill_message,
 };
index 5a171b2acf4371e6d20b6e8ff1347002f4f3a37b..5a8ed6d651223e446fedaa080a570c816204cac5 100644 (file)
@@ -5,7 +5,7 @@
 #include "conf-parser.h"
 #include "qdisc.h"
 
-typedef struct FairQueueTrafficPolicing {
+typedef struct FairQueueing {
         QDisc meta;
 
         uint32_t packet_limit;
@@ -17,13 +17,13 @@ typedef struct FairQueueTrafficPolicing {
         uint32_t orphan_mask;
         int pacing;
         usec_t ce_threshold_usec;
-} FairQueueTrafficPolicing;
+} FairQueueing;
 
-DEFINE_QDISC_CAST(FQ, FairQueueTrafficPolicing);
+DEFINE_QDISC_CAST(FQ, FairQueueing);
 extern const QDiscVTable fq_vtable;
 
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queue_traffic_policing_u32);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queue_traffic_policing_size);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queue_traffic_policing_bool);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queue_traffic_policing_usec);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_fair_queue_traffic_policing_max_rate);
+CONFIG_PARSER_PROTOTYPE(config_parse_fair_queueing_u32);
+CONFIG_PARSER_PROTOTYPE(config_parse_fair_queueing_size);
+CONFIG_PARSER_PROTOTYPE(config_parse_fair_queueing_bool);
+CONFIG_PARSER_PROTOTYPE(config_parse_fair_queueing_usec);
+CONFIG_PARSER_PROTOTYPE(config_parse_fair_queueing_max_rate);
diff --git a/src/network/tc/gred.c b/src/network/tc/gred.c
new file mode 100644 (file)
index 0000000..cca32c3
--- /dev/null
@@ -0,0 +1,193 @@
+/* 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)
+                return log_syntax(unit, LOG_ERR, filename, line, r,
+                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+
+        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_ERR, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        if (v > MAX_DPs) {
+                log_syntax(unit, LOG_ERR, 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)
+                return log_syntax(unit, LOG_ERR, filename, line, r,
+                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+
+        gred = GRED(qdisc);
+
+        if (isempty(rvalue)) {
+                gred->grio = -1;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = parse_boolean(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, 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/htb.c b/src/network/tc/htb.c
new file mode 100644 (file)
index 0000000..f2b9c45
--- /dev/null
@@ -0,0 +1,279 @@
+/* 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"
+
+static int hierarchy_token_bucket_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+        HierarchyTokenBucket *htb;
+        struct tc_htb_glob opt = {
+                .rate2quantum = 10,
+                .version = 3,
+        };
+        int r;
+
+        assert(link);
+        assert(qdisc);
+        assert(req);
+
+        htb = HTB(qdisc);
+
+        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)
+                return log_syntax(unit, LOG_ERR, filename, line, r,
+                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+
+        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_ERR, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        qdisc = NULL;
+
+        return 0;
+}
+
+const QDiscVTable htb_vtable = {
+        .object_size = sizeof(HierarchyTokenBucket),
+        .tca_kind = "htb",
+        .fill_message = hierarchy_token_bucket_fill_message,
+};
+
+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], mtu = 1600; /* Ethernet packet length */
+        int r;
+
+        assert(link);
+        assert(tclass);
+        assert(req);
+
+        htb = TCLASS_TO_HTB(tclass);
+
+        if (htb->ceil_rate == 0)
+                htb->ceil_rate = htb->rate;
+
+        opt.prio = htb->priority;
+        opt.rate.rate = (htb->rate >= (1ULL << 32)) ? ~0U : htb->rate;
+        opt.ceil.rate = (htb->ceil_rate >= (1ULL << 32)) ? ~0U : htb->ceil_rate;
+        r = tc_transmit_time(htb->rate, mtu, &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, mtu, &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, 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, 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_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;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = tclass_new_static(TCLASS_KIND_HTB, network, filename, section_line, &tclass);
+        if (r < 0)
+                return log_syntax(unit, LOG_ERR, filename, line, r,
+                                  "Failed to create traffic control class, ignoring assignment: %m");
+
+        htb = TCLASS_TO_HTB(tclass);
+
+        if (isempty(rvalue)) {
+                htb->priority = 0;
+
+                tclass = NULL;
+                return 0;
+        }
+
+        r = safe_atou32(rvalue, &htb->priority);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        tclass = NULL;
+
+        return 0;
+}
+
+int config_parse_hierarchy_token_bucket_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 < 0)
+                return log_syntax(unit, LOG_ERR, filename, line, r,
+                                  "Failed to create traffic control class, ignoring assignment: %m");
+
+        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_ERR, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        *v /= 8;
+        tclass = NULL;
+
+        return 0;
+}
+
+const TClassVTable htb_tclass_vtable = {
+        .object_size = sizeof(HierarchyTokenBucketClass),
+        .tca_kind = "htb",
+        .fill_message = hierarchy_token_bucket_class_fill_message,
+};
diff --git a/src/network/tc/htb.h b/src/network/tc/htb.h
new file mode 100644 (file)
index 0000000..c8dce2c
--- /dev/null
@@ -0,0 +1,31 @@
+/* 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;
+} HierarchyTokenBucket;
+
+DEFINE_QDISC_CAST(HTB, HierarchyTokenBucket);
+extern const QDiscVTable htb_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_default_class);
+
+typedef struct HierarchyTokenBucketClass {
+        TClass meta;
+
+        uint32_t priority;
+        uint64_t rate;
+        uint64_t ceil_rate;
+} HierarchyTokenBucketClass;
+
+DEFINE_TCLASS_CAST(HTB, HierarchyTokenBucketClass);
+extern const TClassVTable htb_tclass_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_u32);
+CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_rate);
index f74be288e1b2020ab4974bef3f5cd760a1a1ab7c..7d0add9e303f813f46ee3ba70b84c9f7f495a0b1 100644 (file)
@@ -10,7 +10,7 @@
 #include "networkd-manager.h"
 #include "parse-util.h"
 #include "qdisc.h"
-#include "string-util.h"
+#include "strv.h"
 #include "tc-util.h"
 
 static int network_emulator_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
@@ -54,7 +54,7 @@ static int network_emulator_fill_message(Link *link, QDisc *qdisc, sd_netlink_me
         return 0;
 }
 
-int config_parse_tc_network_emulator_delay(
+int config_parse_network_emulator_delay(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -87,9 +87,9 @@ int config_parse_tc_network_emulator_delay(
         ne = NETEM(qdisc);
 
         if (isempty(rvalue)) {
-                if (streq(lvalue, "NetworkEmulatorDelaySec"))
+                if (STR_IN_SET(lvalue, "DelaySec", "NetworkEmulatorDelaySec"))
                         ne->delay = USEC_INFINITY;
-                else if (streq(lvalue, "NetworkEmulatorDelayJitterSec"))
+                else if (STR_IN_SET(lvalue, "DelayJitterSec", "NetworkEmulatorDelayJitterSec"))
                         ne->jitter = USEC_INFINITY;
 
                 qdisc = NULL;
@@ -104,9 +104,9 @@ int config_parse_tc_network_emulator_delay(
                 return 0;
         }
 
-        if (streq(lvalue, "NetworkEmulatorDelaySec"))
+        if (STR_IN_SET(lvalue, "DelaySec", "NetworkEmulatorDelaySec"))
                 ne->delay = u;
-        else if (streq(lvalue, "NetworkEmulatorDelayJitterSec"))
+        else if (STR_IN_SET(lvalue, "DelayJitterSec", "NetworkEmulatorDelayJitterSec"))
                 ne->jitter = u;
 
         qdisc = NULL;
@@ -114,7 +114,7 @@ int config_parse_tc_network_emulator_delay(
         return 0;
 }
 
-int config_parse_tc_network_emulator_rate(
+int config_parse_network_emulator_rate(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -147,9 +147,9 @@ int config_parse_tc_network_emulator_rate(
         ne = NETEM(qdisc);
 
         if (isempty(rvalue)) {
-                if (streq(lvalue, "NetworkEmulatorLossRate"))
+                if (STR_IN_SET(lvalue, "LossRate", "NetworkEmulatorLossRate"))
                         ne->loss = 0;
-                else if (streq(lvalue, "NetworkEmulatorDuplicateRate"))
+                else if (STR_IN_SET(lvalue, "DuplicateRate", "NetworkEmulatorDuplicateRate"))
                         ne->duplicate = 0;
 
                 qdisc = NULL;
@@ -164,16 +164,16 @@ int config_parse_tc_network_emulator_rate(
                 return 0;
         }
 
-        if (streq(lvalue, "NetworkEmulatorLossRate"))
+        if (STR_IN_SET(lvalue, "LossRate", "NetworkEmulatorLossRate"))
                 ne->loss = rate;
-        else if (streq(lvalue, "NetworkEmulatorDuplicateRate"))
+        else if (STR_IN_SET(lvalue, "DuplicateRate", "NetworkEmulatorDuplicateRate"))
                 ne->duplicate = rate;
 
         qdisc = NULL;
         return 0;
 }
 
-int config_parse_tc_network_emulator_packet_limit(
+int config_parse_network_emulator_packet_limit(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -214,8 +214,8 @@ int config_parse_tc_network_emulator_packet_limit(
         r = safe_atou(rvalue, &ne->limit);
         if (r < 0) {
                 log_syntax(unit, LOG_ERR, filename, line, r,
-                           "Failed to parse 'NetworkEmulatorPacketLimit=', ignoring assignment: %s",
-                           rvalue);
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
                 return 0;
         }
 
index 7bf27e34fdba070d0fbd8c671b9c1a8b96c7310f..6319c7252174b3724a172bc93ee149ab532c023e 100644 (file)
@@ -20,6 +20,6 @@ typedef struct NetworkEmulator {
 DEFINE_QDISC_CAST(NETEM, NetworkEmulator);
 extern const QDiscVTable netem_vtable;
 
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_network_emulator_delay);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_network_emulator_rate);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_network_emulator_packet_limit);
+CONFIG_PARSER_PROTOTYPE(config_parse_network_emulator_delay);
+CONFIG_PARSER_PROTOTYPE(config_parse_network_emulator_rate);
+CONFIG_PARSER_PROTOTYPE(config_parse_network_emulator_packet_limit);
index 38086a897554a167fc42208b3ba7cef10b6f3753..31b6a716c4e72c851f1fddbe4a38a009f6ede4ce 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_CAKE] = &cake_vtable,
         [QDISC_KIND_CODEL] = &codel_vtable,
         [QDISC_KIND_FQ] = &fq_vtable,
         [QDISC_KIND_FQ_CODEL] = &fq_codel_vtable,
+        [QDISC_KIND_GRED] = &gred_vtable,
+        [QDISC_KIND_HTB] = &htb_vtable,
         [QDISC_KIND_NETEM] = &netem_vtable,
+        [QDISC_KIND_PFIFO] = &pfifo_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) {
@@ -32,6 +40,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,
@@ -41,6 +50,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;
@@ -60,7 +70,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);
@@ -72,15 +83,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;
                 }
         }
@@ -89,23 +105,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;
 
@@ -118,7 +134,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);
 
@@ -130,22 +146,22 @@ 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;
 
         r = sd_netlink_message_get_errno(m);
         if (r < 0 && r != -EEXIST) {
-                log_link_message_error_errno(link, m, r, "Could not set QDisc: %m");
+                log_link_message_error_errno(link, m, r, "Could not set QDisc");
                 link_enter_failed(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);
         }
 
@@ -176,13 +192,21 @@ int qdisc_configure(Link *link, QDisc *qdisc) {
         }
 
         if (QDISC_VTABLE(qdisc)) {
-                r = sd_netlink_message_append_string(req, TCA_KIND, QDISC_VTABLE(qdisc)->tca_kind);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
+                if (QDISC_VTABLE(qdisc)->fill_tca_kind) {
+                        r = QDISC_VTABLE(qdisc)->fill_tca_kind(link, qdisc, req);
+                        if (r < 0)
+                                return r;
+                } else {
+                        r = sd_netlink_message_append_string(req, TCA_KIND, QDISC_VTABLE(qdisc)->tca_kind);
+                        if (r < 0)
+                                return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
+                }
 
-                r = QDISC_VTABLE(qdisc)->fill_message(link, qdisc, req);
-                if (r < 0)
-                        return r;
+                if (QDISC_VTABLE(qdisc)->fill_message) {
+                        r = QDISC_VTABLE(qdisc)->fill_message(link, qdisc, req);
+                        if (r < 0)
+                                return r;
+                }
         } else {
                 r = sd_netlink_message_append_string(req, TCA_KIND, qdisc->tca_kind);
                 if (r < 0)
@@ -194,7 +218,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;
 }
@@ -218,15 +242,15 @@ int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact) {
         if (qdisc->parent == TC_H_ROOT) {
                 if (*has_root)
                         return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                 "%s: More than one root TrafficControlQueueingDiscipline sections are defined. "
-                                                 "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+                                                 "%s: More than one root qdisc section is defined. "
+                                                 "Ignoring the qdisc section from line %u.",
                                                  qdisc->section->filename, qdisc->section->line);
                 *has_root = true;
         } else if (qdisc->parent == TC_H_CLSACT) { /* TC_H_CLSACT == TC_H_INGRESS */
                 if (*has_clsact)
                         return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                 "%s: More than one clsact or ingress TrafficControlQueueingDiscipline sections are defined. "
-                                                 "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+                                                 "%s: More than one clsact or ingress qdisc section is defined. "
+                                                 "Ignoring the qdisc section from line %u.",
                                                  qdisc->section->filename, qdisc->section->line);
                 *has_clsact = true;
         }
@@ -234,7 +258,7 @@ int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact) {
         return 0;
 }
 
-int config_parse_tc_qdiscs_parent(
+int config_parse_qdisc_parent(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -255,13 +279,14 @@ int config_parse_tc_qdiscs_parent(
         assert(rvalue);
         assert(data);
 
-        r = qdisc_new_static(_QDISC_KIND_INVALID, network, filename, section_line, &qdisc);
+        r = qdisc_new_static(ltype, network, filename, section_line, &qdisc);
         if (r < 0)
                 return r;
 
         if (streq(rvalue, "root")) {
                 qdisc->parent = TC_H_ROOT;
-                qdisc->handle = TC_H_UNSPEC;
+                if (qdisc->handle == 0)
+                        qdisc->handle = TC_H_UNSPEC;
         } else if (streq(rvalue, "clsact")) {
                 qdisc->parent = TC_H_CLSACT;
                 qdisc->handle = TC_H_MAKE(TC_H_CLSACT, 0);
@@ -269,20 +294,68 @@ int config_parse_tc_qdiscs_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_ERR, 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;
+
+        return 0;
+}
+
+int config_parse_qdisc_handle(
+                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;
+        uint16_t n;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(ltype, network, filename, section_line, &qdisc);
+        if (r < 0)
+                return r;
+
+        if (isempty(rvalue)) {
+                qdisc->handle = TC_H_UNSPEC;
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = safe_atou16_full(rvalue, 16, &n);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to parse 'Handle=', ignoring assignment: %s",
+                           rvalue);
+                return 0;
         }
 
+        qdisc->handle = (uint32_t) n << 16;
         qdisc = NULL;
 
         return 0;
index 506c737935b771ea0a791374606c4d1ee54512ec..403d6a5b530c3abc8e504a278a12300b78913911 100644 (file)
@@ -6,19 +6,28 @@
 #include "networkd-link.h"
 #include "networkd-network.h"
 #include "networkd-util.h"
+#include "tc.h"
 
 typedef enum QDiscKind {
+        QDISC_KIND_CAKE,
         QDISC_KIND_CODEL,
         QDISC_KIND_FQ,
         QDISC_KIND_FQ_CODEL,
+        QDISC_KIND_GRED,
+        QDISC_KIND_HTB,
         QDISC_KIND_NETEM,
+        QDISC_KIND_PFIFO,
+        QDISC_KIND_SFB,
         QDISC_KIND_SFQ,
         QDISC_KIND_TBF,
+        QDISC_KIND_TEQL,
         _QDISC_KIND_MAX,
         _QDISC_KIND_INVALID = -1,
 } QDiscKind;
 
 typedef struct QDisc {
+        TrafficControl meta;
+
         NetworkConfigSection *section;
         Network *network;
 
@@ -35,6 +44,7 @@ typedef struct QDiscVTable {
         const char *tca_kind;
         /* called in qdisc_new() */
         int (*init)(QDisc *qdisc);
+        int (*fill_tca_kind)(Link *link, QDisc *qdisc, sd_netlink_message *m);
         int (*fill_message)(Link *link, QDisc *qdisc, sd_netlink_message *m);
         int (*verify)(QDisc *qdisc);
 } QDiscVTable;
@@ -63,11 +73,20 @@ int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact);
 
 DEFINE_NETWORK_SECTION_FUNCTIONS(QDisc, qdisc_free);
 
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_qdiscs_parent);
+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 "fifo.h"
 #include "fq-codel.h"
 #include "fq.h"
+#include "gred.h"
+#include "htb.h"
 #include "netem.h"
+#include "sfb.h"
 #include "sfq.h"
 #include "tbf.h"
+#include "teql.h"
diff --git a/src/network/tc/sfb.c b/src/network/tc/sfb.c
new file mode 100644 (file)
index 0000000..9e954ff
--- /dev/null
@@ -0,0 +1,106 @@
+/* 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)
+                return log_syntax(unit, LOG_ERR, filename, line, r,
+                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+
+        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_ERR, 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 fc0c9bfc27666ca56f8a3178cb2cc1bf3fe2082f..ee66409b94abc2edab5c8ef74a1aeaf471e8338d 100644 (file)
@@ -31,7 +31,7 @@ static int stochastic_fairness_queueing_fill_message(Link *link, QDisc *qdisc, s
         return 0;
 }
 
-int config_parse_tc_stochastic_fairness_queueing_perturb_period(
+int config_parse_stochastic_fairness_queueing_perturb_period(
                 const char *unit,
                 const char *filename,
                 unsigned line,
index d29bcc2e93c552a79249778f4cd0572e63c682ee..19c8decf131ae4dcb25334e6df06cd143752460b 100644 (file)
@@ -15,4 +15,4 @@ typedef struct StochasticFairnessQueueing {
 DEFINE_QDISC_CAST(SFQ, StochasticFairnessQueueing);
 extern const QDiscVTable sfq_vtable;
 
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_stochastic_fairness_queueing_perturb_period);
+CONFIG_PARSER_PROTOTYPE(config_parse_stochastic_fairness_queueing_perturb_period);
index 7dfc5651117e52b5ac93b09f274091683261a3b9..0682ab4cc6575463aa0ad0dc1654149b0d1bfd87 100644 (file)
 #include "tc-util.h"
 #include "util.h"
 
-static int token_buffer_filter_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+static int token_bucket_filter_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
         uint32_t rtab[256], ptab[256];
         struct tc_tbf_qopt opt = {};
-        TokenBufferFilter *tbf;
+        TokenBucketFilter *tbf;
         int r;
 
         assert(link);
@@ -110,7 +110,7 @@ static int token_buffer_filter_fill_message(Link *link, QDisc *qdisc, sd_netlink
         return 0;
 }
 
-int config_parse_tc_token_buffer_filter_size(
+int config_parse_token_bucket_filter_size(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -124,7 +124,7 @@ int config_parse_tc_token_buffer_filter_size(
 
         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
         Network *network = data;
-        TokenBufferFilter *tbf;
+        TokenBucketFilter *tbf;
         uint64_t k;
         int r;
 
@@ -143,17 +143,17 @@ int config_parse_tc_token_buffer_filter_size(
         tbf = TBF(qdisc);
 
         if (isempty(rvalue)) {
-                if (streq(lvalue, "TokenBufferFilterRate"))
+                if (streq(lvalue, "Rate"))
                         tbf->rate = 0;
-                else if (streq(lvalue, "TokenBufferFilterBurst"))
+                else if (streq(lvalue, "Burst"))
                         tbf->burst = 0;
-                else if (streq(lvalue, "TokenBufferFilterLimitSize"))
+                else if (streq(lvalue, "LimitSize"))
                         tbf->limit = 0;
-                else if (streq(lvalue, "TokenBufferFilterMTUBytes"))
+                else if (streq(lvalue, "MTUBytes"))
                         tbf->mtu = 0;
-                else if (streq(lvalue, "TokenBufferFilterMPUBytes"))
+                else if (streq(lvalue, "MPUBytes"))
                         tbf->mpu = 0;
-                else if (streq(lvalue, "TokenBufferFilterPeakRate"))
+                else if (streq(lvalue, "PeakRate"))
                         tbf->peak_rate = 0;
 
                 qdisc = NULL;
@@ -168,17 +168,17 @@ int config_parse_tc_token_buffer_filter_size(
                 return 0;
         }
 
-        if (streq(lvalue, "TokenBufferFilterRate"))
+        if (streq(lvalue, "Rate"))
                 tbf->rate = k / 8;
-        else if (streq(lvalue, "TokenBufferFilterBurst"))
+        else if (streq(lvalue, "Burst"))
                 tbf->burst = k;
-        else if (streq(lvalue, "TokenBufferFilterLimitSize"))
+        else if (streq(lvalue, "LimitSize"))
                 tbf->limit = k;
-        else if (streq(lvalue, "TokenBufferFilterMPUBytes"))
+        else if (streq(lvalue, "MPUBytes"))
                 tbf->mpu = k;
-        else if (streq(lvalue, "TokenBufferFilterMTUBytes"))
+        else if (streq(lvalue, "MTUBytes"))
                 tbf->mtu = k;
-        else if (streq(lvalue, "TokenBufferFilterPeakRate"))
+        else if (streq(lvalue, "PeakRate"))
                 tbf->peak_rate = k / 8;
 
         qdisc = NULL;
@@ -186,7 +186,7 @@ int config_parse_tc_token_buffer_filter_size(
         return 0;
 }
 
-int config_parse_tc_token_buffer_filter_latency(
+int config_parse_token_bucket_filter_latency(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -200,7 +200,7 @@ int config_parse_tc_token_buffer_filter_latency(
 
         _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
         Network *network = data;
-        TokenBufferFilter *tbf;
+        TokenBucketFilter *tbf;
         usec_t u;
         int r;
 
@@ -240,45 +240,45 @@ int config_parse_tc_token_buffer_filter_latency(
         return 0;
 }
 
-static int token_buffer_filter_verify(QDisc *qdisc) {
-        TokenBufferFilter *tbf = TBF(qdisc);
+static int token_bucket_filter_verify(QDisc *qdisc) {
+        TokenBucketFilter *tbf = TBF(qdisc);
 
         if (tbf->limit > 0 && tbf->latency > 0)
                 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                         "%s: Specifying both TokenBufferFilterLimitSize= and TokenBufferFilterLatencySec= is not allowed. "
-                                         "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+                                         "%s: Specifying both LimitSize= and LatencySec= is not allowed. "
+                                         "Ignoring [TokenBucketFilter] section from line %u.",
                                          qdisc->section->filename, qdisc->section->line);
 
         if (tbf->limit == 0 && tbf->latency == 0)
                 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                         "%s: Either TokenBufferFilterLimitSize= or TokenBufferFilterLatencySec= is required. "
-                                         "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+                                         "%s: Either LimitSize= or LatencySec= is required. "
+                                         "Ignoring [TokenBucketFilter] section from line %u.",
                                          qdisc->section->filename, qdisc->section->line);
 
         if (tbf->rate == 0)
                 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                         "%s: TokenBufferFilterRate= is mandatory. "
-                                         "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+                                         "%s: Rate= is mandatory. "
+                                         "Ignoring [TokenBucketFilter] section from line %u.",
                                          qdisc->section->filename, qdisc->section->line);
 
         if (tbf->burst == 0)
                 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                         "%s: TokenBufferFilterBurst= is mandatory. "
-                                         "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+                                         "%s: Burst= is mandatory. "
+                                         "Ignoring [TokenBucketFilter] section from line %u.",
                                          qdisc->section->filename, qdisc->section->line);
 
         if (tbf->peak_rate > 0 && tbf->mtu == 0)
                 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                         "%s: TokenBufferFilterMTUBytes= is mandatory when TokenBufferFilterPeakRate= is specified. "
-                                         "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+                                         "%s: MTUBytes= is mandatory when PeakRate= is specified. "
+                                         "Ignoring [TokenBucketFilter] section from line %u.",
                                          qdisc->section->filename, qdisc->section->line);
 
         return 0;
 }
 
 const QDiscVTable tbf_vtable = {
-        .object_size = sizeof(TokenBufferFilter),
+        .object_size = sizeof(TokenBucketFilter),
         .tca_kind = "tbf",
-        .fill_message = token_buffer_filter_fill_message,
-        .verify = token_buffer_filter_verify
+        .fill_message = token_bucket_filter_fill_message,
+        .verify = token_bucket_filter_verify
 };
index 317dc0310722acbb0932656fd98a67c51fe943a6..b66aef206c49b7455703262ead378d81b12c0964 100644 (file)
@@ -6,7 +6,7 @@
 #include "qdisc.h"
 #include "time-util.h"
 
-typedef struct TokenBufferFilter {
+typedef struct TokenBucketFilter {
         QDisc meta;
 
         uint64_t rate;
@@ -16,10 +16,10 @@ typedef struct TokenBufferFilter {
         usec_t latency;
         size_t limit;
         size_t mpu;
-} TokenBufferFilter;
+} TokenBucketFilter;
 
-DEFINE_QDISC_CAST(TBF, TokenBufferFilter);
+DEFINE_QDISC_CAST(TBF, TokenBucketFilter);
 extern const QDiscVTable tbf_vtable;
 
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_token_buffer_filter_latency);
-CONFIG_PARSER_PROTOTYPE(config_parse_tc_token_buffer_filter_size);
+CONFIG_PARSER_PROTOTYPE(config_parse_token_bucket_filter_latency);
+CONFIG_PARSER_PROTOTYPE(config_parse_token_bucket_filter_size);
index c46550f955942907b7ba8f99c1d7be9c61cc1650..47371a841b974432005af046e4c47cf4524de214 100644 (file)
@@ -2,6 +2,7 @@
  * Copyright © 2019 VMware, Inc. */
 
 #include "alloc-util.h"
+#include "extract-word.h"
 #include "fileio.h"
 #include "parse-util.h"
 #include "tc-util.h"
@@ -92,3 +93,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..38b9d0786d58aa9fdd814ad9c3c6a6b6e63c0bd9 100644 (file)
@@ -10,3 +10,4 @@ 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..87f5bcf
--- /dev/null
@@ -0,0 +1,277 @@
+/* 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_HTB] = &htb_tclass_vtable,
+};
+
+static int tclass_new(TClassKind kind, TClass **ret) {
+        TClass *tclass;
+        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 < 0)
+                return r;
+
+        if (streq(rvalue, "root"))
+                tclass->parent = TC_H_ROOT;
+        else {
+                r = parse_handle(rvalue, &tclass->parent);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, 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 < 0)
+                return r;
+
+        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_ERR, 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..9698e58
--- /dev/null
@@ -0,0 +1,67 @@
+/* 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_HTB,
+        _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 "htb.h"
diff --git a/src/network/tc/teql.c b/src/network/tc/teql.c
new file mode 100644 (file)
index 0000000..aac9499
--- /dev/null
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "macro.h"
+#include "netlink-util.h"
+#include "parse-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "teql.h"
+
+static int trivial_link_equalizer_fill_tca_kind(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+        char kind[STRLEN("teql") + DECIMAL_STR_MAX(unsigned)];
+        TrivialLinkEqualizer *teql;
+        int r;
+
+        assert(link);
+        assert(qdisc);
+        assert(req);
+
+        teql = TEQL(qdisc);
+
+        xsprintf(kind, "teql%u", teql->id);
+        r = sd_netlink_message_append_string(req, TCA_KIND, kind);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
+
+        return 0;
+}
+
+const QDiscVTable teql_vtable = {
+        .object_size = sizeof(TrivialLinkEqualizer),
+        .fill_tca_kind = trivial_link_equalizer_fill_tca_kind,
+};
+
+int config_parse_trivial_link_equalizer_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) {
+
+        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+        TrivialLinkEqualizer *teql;
+        Network *network = data;
+        unsigned id;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        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");
+
+        teql = TEQL(qdisc);
+
+        if (isempty(rvalue)) {
+                teql->id = 0;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = safe_atou(rvalue, &id);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, 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,
+                           "'%s=' is too large, ignoring assignment: %s",
+                           lvalue, rvalue);
+        }
+
+        teql->id = id;
+
+        qdisc = NULL;
+        return 0;
+}
diff --git a/src/network/tc/teql.h b/src/network/tc/teql.h
new file mode 100644 (file)
index 0000000..5b091aa
--- /dev/null
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "conf-parser.h"
+#include "qdisc.h"
+
+typedef struct TrivialLinkEqualizer {
+        QDisc meta;
+
+        unsigned id;
+} TrivialLinkEqualizer;
+
+DEFINE_QDISC_CAST(TEQL, TrivialLinkEqualizer);
+extern const QDiscVTable teql_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_trivial_link_equalizer_id);
index 7c37563ac22ae0a694b97024dd48a79af1dcbf90..e23bec76807a910bdf17f814d241ee13a7cb8529 100644 (file)
@@ -122,11 +122,18 @@ static int test_load_config(Manager *manager) {
 static void test_network_get(Manager *manager, sd_device *loopback) {
         Network *network;
         const struct ether_addr mac = ETHER_ADDR_NULL;
+        int r;
 
-        /* let's assume that the test machine does not have a .network file
-           that applies to the loopback device... */
-        assert_se(network_get(manager, loopback, "lo", NULL, &mac, &mac, 0, NULL, NULL, &network) == -ENOENT);
-        assert_se(!network);
+        /* Let's hope that the test machine does not have a .network file that applies to loopback device…
+         * But it is still possible, so let's allow that case too. */
+        r = network_get(manager, 0, loopback, "lo", NULL, &mac, &mac, 0, NULL, NULL, &network);
+        if (r == -ENOENT)
+                /* The expected case */
+                assert_se(!network);
+        else if (r >= 0)
+                assert_se(network);
+        else
+                assert_not_reached("bad error!");
 }
 
 static void test_address_equality(void) {
index a13373f7d7bec16a14f0057786656ad686a98cb0..69b0057707a52c9e68dc80ab51b862816891cc25 100644 (file)
@@ -36,7 +36,7 @@ int link_new(Manager *m, Link **ret, int ifindex, const char *ifname) {
                 .manager = m,
                 .ifname = TAKE_PTR(n),
                 .ifindex = ifindex,
-                .required_operstate = LINK_OPERSTATE_DEGRADED,
+                .required_operstate = LINK_OPERSTATE_RANGE_DEFAULT,
         };
 
         r = hashmap_put(m->links_by_name, l->ifname, l);
@@ -105,7 +105,6 @@ int link_update_rtnl(Link *l, sd_netlink_message *m) {
 
 int link_update_monitor(Link *l) {
         _cleanup_free_ char *operstate = NULL, *required_operstate = NULL, *state = NULL;
-        LinkOperationalState s;
         int r, ret = 0;
 
         assert(l);
@@ -121,19 +120,21 @@ int link_update_monitor(Link *l) {
         r = sd_network_link_get_required_operstate_for_online(l->ifindex, &required_operstate);
         if (r < 0)
                 ret = log_link_debug_errno(l, r, "Failed to get required operational state, ignoring: %m");
+        else if (isempty(required_operstate))
+                l->required_operstate = LINK_OPERSTATE_RANGE_DEFAULT;
         else {
-                s = link_operstate_from_string(required_operstate);
-                if (s < 0)
+                r = parse_operational_state_range(required_operstate, &l->required_operstate);
+                if (r < 0)
                         ret = log_link_debug_errno(l, SYNTHETIC_ERRNO(EINVAL),
                                                    "Failed to parse required operational state, ignoring: %m");
-                else
-                        l->required_operstate = s;
         }
 
         r = sd_network_link_get_operational_state(l->ifindex, &operstate);
         if (r < 0)
                 ret = log_link_debug_errno(l, r, "Failed to get operational state, ignoring: %m");
         else {
+                LinkOperationalState s;
+
                 s = link_operstate_from_string(operstate);
                 if (s < 0)
                         ret = log_link_debug_errno(l, SYNTHETIC_ERRNO(EINVAL),
index d58129dfe872f658fa1ea8d68f0cd24fdc9b9e55..73d9f9cc3ec8d2213da93774f8dbe2e2928cb38c 100644 (file)
@@ -17,7 +17,7 @@ struct Link {
         unsigned flags;
 
         bool required_for_online;
-        LinkOperationalState required_operstate;
+        LinkOperationalStateRange required_operstate;
         LinkOperationalState operational_state;
         char *state;
 };
index 6de3df94e1f2b2334ae4c48805224b6258a2df9c..40a29f19aa0d098ffc529cf45539189114c5e2e1 100644 (file)
@@ -29,10 +29,10 @@ static bool manager_ignore_link(Manager *m, Link *link) {
                 return true;
 
         /* ignore interfaces we explicitly are asked to ignore */
-        return strv_fnmatch(m->ignore, link->ifname, 0);
+        return strv_fnmatch(m->ignore, link->ifname);
 }
 
-static int manager_link_is_online(Manager *m, Link *l, LinkOperationalState s) {
+static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange s) {
         /* This returns the following:
          * -EAGAIN: not processed by udev or networkd
          *       0: operstate is not enough
@@ -46,13 +46,18 @@ static int manager_link_is_online(Manager *m, Link *l, LinkOperationalState s) {
                 return log_link_debug_errno(l, SYNTHETIC_ERRNO(EAGAIN),
                                             "link is being processed by networkd");
 
-        if (s < 0)
-                s = m->required_operstate >= 0 ? m->required_operstate : l->required_operstate;
+        if (s.min < 0)
+                s.min = m->required_operstate.min >= 0 ? m->required_operstate.min
+                                                       : l->required_operstate.min;
 
-        if (l->operational_state < s) {
-                log_link_debug(l, "Operational state '%s' is below '%s'",
+        if (s.max < 0)
+                s.max = m->required_operstate.max >= 0 ? m->required_operstate.max
+                                                       : l->required_operstate.max;
+
+        if (l->operational_state < s.min || l->operational_state > s.max) {
+                log_link_debug(l, "Operational state '%s' is not in range ['%s':'%s']",
                                link_operstate_to_string(l->operational_state),
-                               link_operstate_to_string(s));
+                               link_operstate_to_string(s.min), link_operstate_to_string(s.max));
                 return 0;
         }
 
@@ -70,9 +75,14 @@ bool manager_configured(Manager *m) {
         if (!hashmap_isempty(m->interfaces)) {
                 /* wait for all the links given on the command line to appear */
                 HASHMAP_FOREACH_KEY(p, ifname, m->interfaces, i) {
-                        LinkOperationalState s = PTR_TO_INT(p);
+                        LinkOperationalStateRange *range = p;
 
                         l = hashmap_get(m->links_by_name, ifname);
+                        if (!l && range->min == LINK_OPERSTATE_MISSING) {
+                                one_ready = true;
+                                continue;
+                        }
+
                         if (!l) {
                                 log_debug("still waiting for %s", ifname);
                                 if (!m->any)
@@ -80,7 +90,7 @@ bool manager_configured(Manager *m) {
                                 continue;
                         }
 
-                        if (manager_link_is_online(m, l, s) <= 0) {
+                        if (manager_link_is_online(m, l, *range) <= 0) {
                                 if (!m->any)
                                         return false;
                                 continue;
@@ -102,7 +112,9 @@ bool manager_configured(Manager *m) {
                         continue;
                 }
 
-                r = manager_link_is_online(m, l, _LINK_OPERSTATE_INVALID);
+                r = manager_link_is_online(m, l,
+                                           (LinkOperationalStateRange) { _LINK_OPERSTATE_INVALID,
+                                                                         _LINK_OPERSTATE_INVALID });
                 if (r < 0 && !m->any)
                         return false;
                 if (r > 0)
@@ -289,7 +301,7 @@ static int manager_network_monitor_listen(Manager *m) {
 }
 
 int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
-                LinkOperationalState required_operstate,
+                LinkOperationalStateRange required_operstate,
                 bool any, usec_t timeout) {
         _cleanup_(manager_freep) Manager *m = NULL;
         int r;
index dd7d847dd385636e4929c46dfe07091c21041fe0..7398783df713893778270d9db7c1d65cdac321d5 100644 (file)
@@ -20,7 +20,7 @@ struct Manager {
         Hashmap *interfaces;
         char **ignore;
 
-        LinkOperationalState required_operstate;
+        LinkOperationalStateRange required_operstate;
         bool any;
 
         sd_netlink *rtnl;
@@ -34,7 +34,7 @@ struct Manager {
 
 void manager_free(Manager *m);
 int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
-                LinkOperationalState required_operstate,
+                LinkOperationalStateRange required_operstate,
                 bool any, usec_t timeout);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
index 4ce2ac31b2d60ae6f2506d27dc58645e66cd633b..17ed5d38cfe3de6bd53344cc7f391f90e9f61c13 100644 (file)
@@ -18,10 +18,10 @@ static bool arg_quiet = false;
 static usec_t arg_timeout = 120 * USEC_PER_SEC;
 static Hashmap *arg_interfaces = NULL;
 static char **arg_ignore = NULL;
-static LinkOperationalState arg_required_operstate = _LINK_OPERSTATE_INVALID;
+static LinkOperationalStateRange arg_required_operstate = { _LINK_OPERSTATE_INVALID, _LINK_OPERSTATE_INVALID };
 static bool arg_any = false;
 
-STATIC_DESTRUCTOR_REGISTER(arg_interfaces, hashmap_free_free_keyp);
+STATIC_DESTRUCTOR_REGISTER(arg_interfaces, hashmap_free_free_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_ignore, strv_freep);
 
 static int help(void) {
@@ -37,10 +37,10 @@ static int help(void) {
                "  -h --help                 Show this help\n"
                "     --version              Print version string\n"
                "  -q --quiet                Do not show status information\n"
-               "  -i --interface=INTERFACE[:OPERSTATE]\n"
+               "  -i --interface=INTERFACE[:MIN_OPERSTATE[:MAX_OPERSTATE]]\n"
                "                            Block until at least these interfaces have appeared\n"
                "     --ignore=INTERFACE     Don't take these interfaces into account\n"
-               "  -o --operational-state=OPERSTATE\n"
+               "  -o --operational-state=MIN_OPERSTATE[:MAX_OPERSTATE]\n"
                "                            Required operational state\n"
                "     --any                  Wait until at least one of the interfaces is online\n"
                "     --timeout=SECS         Maximum time to wait for network connectivity\n"
@@ -52,28 +52,28 @@ static int help(void) {
         return 0;
 }
 
-static int parse_interface_with_operstate(const char *str) {
+static int parse_interface_with_operstate_range(const char *str) {
         _cleanup_free_ char *ifname = NULL;
-        LinkOperationalState s;
+        _cleanup_free_ LinkOperationalStateRange *range;
         const char *p;
         int r;
 
         assert(str);
 
+        range = new(LinkOperationalStateRange, 1);
+        if (!range)
+                return log_oom();
+
         p = strchr(str, ':');
         if (p) {
-                if (isempty(p + 1))
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                               "Operational state is empty.");
-
-                s = link_operstate_from_string(p + 1);
-                if (s < 0)
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                               "Invalid operational state '%s'", p + 1);
+                r = parse_operational_state_range(p + 1, range);
+                if (r < 0)
+                         log_error_errno(r, "Invalid operational state range '%s'", p + 1);
 
                 ifname = strndup(optarg, p - optarg);
         } else {
-                s = _LINK_OPERSTATE_INVALID;
+                range->min = _LINK_OPERSTATE_INVALID;
+                range->max = _LINK_OPERSTATE_INVALID;
                 ifname = strdup(str);
         }
         if (!ifname)
@@ -87,7 +87,7 @@ static int parse_interface_with_operstate(const char *str) {
         if (r < 0)
                 return log_oom();
 
-        r = hashmap_put(arg_interfaces, ifname, INT_TO_PTR(s));
+        r = hashmap_put(arg_interfaces, ifname, TAKE_PTR(range));
         if (r < 0)
                 return log_error_errno(r, "Failed to store interface name: %m");
         if (r == 0)
@@ -140,7 +140,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return version();
 
                 case 'i':
-                        r = parse_interface_with_operstate(optarg);
+                        r = parse_interface_with_operstate_range(optarg);
                         if (r < 0)
                                 return r;
                         break;
@@ -152,14 +152,14 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case 'o': {
-                        LinkOperationalState s;
+                        LinkOperationalStateRange range;
+
+                        r = parse_operational_state_range(optarg, &range);
+                        if (r < 0)
+                                return log_error_errno(r, "Invalid operational state range '%s'", optarg);
 
-                        s = link_operstate_from_string(optarg);
-                        if (s < 0)
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                       "Invalid operational state '%s'", optarg);
+                        arg_required_operstate = range;
 
-                        arg_required_operstate = s;
                         break;
                 }
                 case ARG_ANY:
index 6afc31d824b345a3deecd9456a6caf677c9be7dc..a862355a64598d32737dd8e99b0713ccdfa70a1d 100644 (file)
@@ -906,7 +906,7 @@ static int mount_inaccessible(const char *dest, CustomMount *m) {
 
         r = mount_verbose(m->graceful ? LOG_DEBUG : LOG_ERR, NULL, where, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL);
         if (r < 0) {
-                umount_verbose(where);
+                (void) umount_verbose(where);
                 return m->graceful ? 0 : r;
         }
 
index 791bdd35321a64f171603ed0f3d516ad213ef7b7..734dee1130e02e4a9ba91186fc6c47a81ed9a424 100644 (file)
@@ -3000,7 +3000,7 @@ static int inner_child(
                 return log_error_errno(errno, "setsid() failed: %m");
 
         if (arg_private_network)
-                loopback_setup();
+                (void) loopback_setup();
 
         if (arg_expose_ports) {
                 r = expose_port_send_rtnl(rtnl_socket);
@@ -3020,7 +3020,7 @@ static int inner_child(
 
                 r = setup_dev_console(console);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to setup /dev/console: %m");
+                        return log_error_errno(r, "Failed to set up /dev/console: %m");
 
                 r = send_one_fd(master_pty_socket, master, 0);
                 if (r < 0)
@@ -3309,10 +3309,12 @@ static int outer_child(
 
                 r = dissected_image_mount(dissected_image, directory, arg_uid_shift,
                                           DISSECT_IMAGE_MOUNT_ROOT_ONLY|DISSECT_IMAGE_DISCARD_ON_LOOP|
-                                          (arg_read_only ? DISSECT_IMAGE_READ_ONLY : 0)|
+                                          (arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK)|
                                           (arg_start_mode == START_BOOT ? DISSECT_IMAGE_VALIDATE_OS : 0));
+                if (r == -EUCLEAN)
+                        return log_error_errno(r, "File system check for image failed: %m");
                 if (r < 0)
-                        return r;
+                        return log_error_errno(r, "Failed to mount image root file system: %m");
         }
 
         r = determine_uid_shift(directory);
@@ -3396,9 +3398,11 @@ static int outer_child(
         if (dissected_image) {
                 /* Now we know the uid shift, let's now mount everything else that might be in the image. */
                 r = dissected_image_mount(dissected_image, directory, arg_uid_shift,
-                                          DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY|DISSECT_IMAGE_DISCARD_ON_LOOP|(arg_read_only ? DISSECT_IMAGE_READ_ONLY : 0));
+                                          DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY|DISSECT_IMAGE_DISCARD_ON_LOOP|(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK));
+                if (r == -EUCLEAN)
+                        return log_error_errno(r, "File system check for image failed: %m");
                 if (r < 0)
-                        return r;
+                        return log_error_errno(r, "Failed to mount image file system: %m");
         }
 
         if (arg_unified_cgroup_hierarchy == CGROUP_UNIFIED_UNKNOWN) {
@@ -5112,14 +5116,14 @@ static int run(int argc, char *argv[]) {
                                 loop->fd,
                                 arg_image,
                                 arg_root_hash, arg_root_hash_size,
-                                DISSECT_IMAGE_REQUIRE_ROOT,
+                                DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK,
                                 &dissected_image);
                 if (r == -ENOPKG) {
                         /* dissected_image_and_warn() already printed a brief error message. Extend on that with more details */
                         log_notice("Note that the disk image needs to\n"
                                    "    a) either contain only a single MBR partition of type 0x83 that is marked bootable\n"
                                    "    b) or contain a single GPT partition of type 0FC63DAF-8483-4772-8E79-3D69D8477DE4\n"
-                                   "    c) or follow http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/\n"
+                                   "    c) or follow https://systemd.io/DISCOVERABLE_PARTITIONS\n"
                                    "    d) or contain a file system without a partition table\n"
                                    "in order to be bootable with systemd-nspawn.");
                         goto finish;
index 8ef1cd5ea9e7413d09eed822ec20b657ff0d3b55..9c004616f599a6d5e4bd384248ecf9ae40f328bc 100644 (file)
@@ -3,28 +3,17 @@
 #include <nss.h>
 #include <pthread.h>
 
-#include "sd-bus.h"
-
-#include "alloc-util.h"
-#include "bus-common-errors.h"
-#include "dirent-util.h"
 #include "env-util.h"
+#include "errno-util.h"
 #include "fd-util.h"
-#include "format-util.h"
-#include "fs-util.h"
-#include "list.h"
+#include "group-record-nss.h"
 #include "macro.h"
 #include "nss-util.h"
 #include "signal-util.h"
-#include "stdio-util.h"
-#include "string-util.h"
+#include "strv.h"
 #include "user-util.h"
-#include "util.h"
-
-#define DYNAMIC_USER_GECOS       "Dynamic User"
-#define DYNAMIC_USER_PASSWD      "*" /* locked */
-#define DYNAMIC_USER_DIR         "/"
-#define DYNAMIC_USER_SHELL       NOLOGIN
+#include "userdb-glue.h"
+#include "userdb.h"
 
 static const struct passwd root_passwd = {
         .pw_name = (char*) "root",
@@ -60,78 +49,32 @@ static const struct group nobody_group = {
         .gr_mem = (char*[]) { NULL },
 };
 
-typedef struct UserEntry UserEntry;
-typedef struct GetentData GetentData;
+typedef struct GetentData {
+        /* As explained in NOTES section of getpwent_r(3) as 'getpwent_r() is not really reentrant since it
+         * shares the reading position in the stream with all other threads', we need to protect the data in
+         * UserDBIterator from multithreaded programs which may call setpwent(), getpwent_r(), or endpwent()
+         * simultaneously. So, each function locks the data by using the mutex below. */
+        pthread_mutex_t mutex;
+        UserDBIterator *iterator;
 
-struct UserEntry {
-        uid_t id;
-        char *name;
+        /* Applies to group iterations only: true while we iterate over groups defined through NSS, false
+         * otherwise. */
+        bool by_membership;
+} GetentData;
 
-        GetentData *data;
-        LIST_FIELDS(UserEntry, entries);
+static GetentData getpwent_data = {
+        .mutex = PTHREAD_MUTEX_INITIALIZER
 };
 
-struct GetentData {
-        /* As explained in NOTES section of getpwent_r(3) as 'getpwent_r() is not really
-         * reentrant since it shares the reading position in the stream with all other threads',
-         * we need to protect the data in UserEntry from multithreaded programs which may call
-         * setpwent(), getpwent_r(), or endpwent() simultaneously. So, each function locks the
-         * data by using the mutex below. */
-        pthread_mutex_t mutex;
-
-        UserEntry *position;
-        LIST_HEAD(UserEntry, entries);
+static GetentData getgrent_data = {
+        .mutex = PTHREAD_MUTEX_INITIALIZER
 };
 
-static GetentData getpwent_data = { PTHREAD_MUTEX_INITIALIZER, NULL, NULL };
-static GetentData getgrent_data = { PTHREAD_MUTEX_INITIALIZER, NULL, NULL };
-
 NSS_GETPW_PROTOTYPES(systemd);
 NSS_GETGR_PROTOTYPES(systemd);
-enum nss_status _nss_systemd_endpwent(void) _public_;
-enum nss_status _nss_systemd_setpwent(int stayopen) _public_;
-enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) _public_;
-enum nss_status _nss_systemd_endgrent(void) _public_;
-enum nss_status _nss_systemd_setgrent(int stayopen) _public_;
-enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size_t buflen, int *errnop) _public_;
-
-static int direct_lookup_name(const char *name, uid_t *ret) {
-        _cleanup_free_ char *s = NULL;
-        const char *path;
-        int r;
-
-        assert(name);
-
-        /* Normally, we go via the bus to resolve names. That has the benefit that it is available from any mount
-         * namespace and subject to proper authentication. However, there's one problem: if our module is called from
-         * dbus-daemon itself we really can't use D-Bus to communicate. In this case, resort to a client-side hack,
-         * and look for the dynamic names directly. This is pretty ugly, but breaks the cyclic dependency. */
-
-        path = strjoina("/run/systemd/dynamic-uid/direct:", name);
-        r = readlink_malloc(path, &s);
-        if (r < 0)
-                return r;
-
-        return parse_uid(s, ret);
-}
-
-static int direct_lookup_uid(uid_t uid, char **ret) {
-        char path[STRLEN("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1], *s;
-        int r;
-
-        xsprintf(path, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid);
-
-        r = readlink_malloc(path, &s);
-        if (r < 0)
-                return r;
-        if (!valid_user_group_name(s)) { /* extra safety check */
-                free(s);
-                return -EINVAL;
-        }
-
-        *ret = s;
-        return 0;
-}
+NSS_PWENT_PROTOTYPES(systemd);
+NSS_GRENT_PROTOTYPES(systemd);
+NSS_INITGROUPS_PROTOTYPE(systemd);
 
 enum nss_status _nss_systemd_getpwnam_r(
                 const char *name,
@@ -139,99 +82,49 @@ enum nss_status _nss_systemd_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;
-        uint32_t translated;
-        size_t l;
-        int bypass, r;
+        enum nss_status status;
+        int e;
 
         PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         assert(name);
         assert(pwd);
+        assert(errnop);
 
-        /* If the username is not valid, then we don't know it. Ideally libc would filter these for us anyway. We don't
-         * generate EINVAL here, because it isn't really out business to complain about invalid user names. */
+        /* If the username is not valid, then we don't know it. Ideally libc would filter these for us
+         * anyway. We don't generate EINVAL here, because it isn't really out business to complain about
+         * invalid user names. */
         if (!valid_user_group_name(name))
                 return NSS_STATUS_NOTFOUND;
 
         /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
         if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
+
                 if (streq(name, root_passwd.pw_name)) {
                         *pwd = root_passwd;
                         return NSS_STATUS_SUCCESS;
                 }
-                if (synthesize_nobody() &&
-                    streq(name, nobody_passwd.pw_name)) {
-                        *pwd = nobody_passwd;
-                        return NSS_STATUS_SUCCESS;
-                }
-        }
-
-        /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
-        if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
-                return NSS_STATUS_NOTFOUND;
 
-        bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
-        if (bypass <= 0) {
-                r = sd_bus_open_system(&bus);
-                if (r < 0)
-                        bypass = 1;
-        }
-
-        if (bypass > 0) {
-                r = direct_lookup_name(name, (uid_t*) &translated);
-                if (r == -ENOENT)
-                        return NSS_STATUS_NOTFOUND;
-                if (r < 0)
-                        goto fail;
-        } else {
-                r = sd_bus_call_method(bus,
-                                       "org.freedesktop.systemd1",
-                                       "/org/freedesktop/systemd1",
-                                       "org.freedesktop.systemd1.Manager",
-                                       "LookupDynamicUserByName",
-                                       &error,
-                                       &reply,
-                                       "s",
-                                       name);
-                if (r < 0) {
-                        if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+                if (streq(name, nobody_passwd.pw_name)) {
+                        if (!synthesize_nobody())
                                 return NSS_STATUS_NOTFOUND;
 
-                        goto fail;
+                        *pwd = nobody_passwd;
+                        return NSS_STATUS_SUCCESS;
                 }
 
-                r = sd_bus_message_read(reply, "u", &translated);
-                if (r < 0)
-                        goto fail;
-        }
+        } else if (STR_IN_SET(name, root_passwd.pw_name, nobody_passwd.pw_name))
+                return NSS_STATUS_NOTFOUND;
 
-        l = strlen(name);
-        if (buflen < l+1) {
+        status = userdb_getpwnam(name, pwd, buffer, buflen, &e);
+        if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
                 UNPROTECT_ERRNO;
-                *errnop = ERANGE;
-                return NSS_STATUS_TRYAGAIN;
+                *errnop = e;
+                return status;
         }
 
-        memcpy(buffer, name, l+1);
-
-        pwd->pw_name = buffer;
-        pwd->pw_uid = (uid_t) translated;
-        pwd->pw_gid = (uid_t) translated;
-        pwd->pw_gecos = (char*) DYNAMIC_USER_GECOS;
-        pwd->pw_passwd = (char*) DYNAMIC_USER_PASSWD;
-        pwd->pw_dir = (char*) DYNAMIC_USER_DIR;
-        pwd->pw_shell = (char*) DYNAMIC_USER_SHELL;
-
-        return NSS_STATUS_SUCCESS;
-
-fail:
-        UNPROTECT_ERRNO;
-        *errnop = -r;
-        return NSS_STATUS_UNAVAIL;
+        return status;
 }
 
 enum nss_status _nss_systemd_getpwuid_r(
@@ -240,100 +133,45 @@ enum nss_status _nss_systemd_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;
-        _cleanup_free_ char *direct = NULL;
-        const char *translated;
-        size_t l;
-        int bypass, r;
+        enum nss_status status;
+        int e;
 
         PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
+        assert(pwd);
+        assert(errnop);
+
         if (!uid_is_valid(uid))
                 return NSS_STATUS_NOTFOUND;
 
         /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
         if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
+
                 if (uid == root_passwd.pw_uid) {
                         *pwd = root_passwd;
                         return NSS_STATUS_SUCCESS;
                 }
-                if (synthesize_nobody() &&
-                    uid == nobody_passwd.pw_uid) {
-                        *pwd = nobody_passwd;
-                        return NSS_STATUS_SUCCESS;
-                }
-        }
 
-        if (!uid_is_dynamic(uid))
-                return NSS_STATUS_NOTFOUND;
-
-        if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
-                return NSS_STATUS_NOTFOUND;
-
-        bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
-        if (bypass <= 0) {
-                r = sd_bus_open_system(&bus);
-                if (r < 0)
-                        bypass = 1;
-        }
-
-        if (bypass > 0) {
-                r = direct_lookup_uid(uid, &direct);
-                if (r == -ENOENT)
-                        return NSS_STATUS_NOTFOUND;
-                if (r < 0)
-                        goto fail;
-
-                translated = direct;
-
-        } else {
-                r = sd_bus_call_method(bus,
-                                       "org.freedesktop.systemd1",
-                                       "/org/freedesktop/systemd1",
-                                       "org.freedesktop.systemd1.Manager",
-                                       "LookupDynamicUserByUID",
-                                       &error,
-                                       &reply,
-                                       "u",
-                                       (uint32_t) uid);
-                if (r < 0) {
-                        if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+                if (uid == nobody_passwd.pw_uid) {
+                        if (!synthesize_nobody())
                                 return NSS_STATUS_NOTFOUND;
 
-                        goto fail;
+                        *pwd = nobody_passwd;
+                        return NSS_STATUS_SUCCESS;
                 }
 
-                r = sd_bus_message_read(reply, "s", &translated);
-                if (r < 0)
-                        goto fail;
-        }
+        } else if (uid == root_passwd.pw_uid || uid == nobody_passwd.pw_uid)
+                return NSS_STATUS_NOTFOUND;
 
-        l = strlen(translated) + 1;
-        if (buflen < l) {
+        status = userdb_getpwuid(uid, pwd, buffer, buflen, &e);
+        if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
                 UNPROTECT_ERRNO;
-                *errnop = ERANGE;
-                return NSS_STATUS_TRYAGAIN;
+                *errnop = e;
+                return status;
         }
 
-        memcpy(buffer, translated, l);
-
-        pwd->pw_name = buffer;
-        pwd->pw_uid = uid;
-        pwd->pw_gid = uid;
-        pwd->pw_gecos = (char*) DYNAMIC_USER_GECOS;
-        pwd->pw_passwd = (char*) DYNAMIC_USER_PASSWD;
-        pwd->pw_dir = (char*) DYNAMIC_USER_DIR;
-        pwd->pw_shell = (char*) DYNAMIC_USER_SHELL;
-
-        return NSS_STATUS_SUCCESS;
-
-fail:
-        UNPROTECT_ERRNO;
-        *errnop = -r;
-        return NSS_STATUS_UNAVAIL;
+        return status;
 }
 
 #pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
@@ -344,94 +182,46 @@ enum nss_status _nss_systemd_getgrnam_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;
-        uint32_t translated;
-        size_t l;
-        int bypass, r;
+        enum nss_status status;
+        int e;
 
         PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         assert(name);
         assert(gr);
+        assert(errnop);
 
         if (!valid_user_group_name(name))
                 return NSS_STATUS_NOTFOUND;
 
-        /* Synthesize records for root and nobody, in case they are missing form /etc/group */
+        /* Synthesize records for root and nobody, in case they are missing from /etc/group */
         if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
+
                 if (streq(name, root_group.gr_name)) {
                         *gr = root_group;
                         return NSS_STATUS_SUCCESS;
                 }
-                if (synthesize_nobody() &&
-                    streq(name, nobody_group.gr_name)) {
-                        *gr = nobody_group;
-                        return NSS_STATUS_SUCCESS;
-                }
-        }
-
-        if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
-                return NSS_STATUS_NOTFOUND;
-
-        bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
-        if (bypass <= 0) {
-                r = sd_bus_open_system(&bus);
-                if (r < 0)
-                        bypass = 1;
-        }
 
-        if (bypass > 0) {
-                r = direct_lookup_name(name, (uid_t*) &translated);
-                if (r == -ENOENT)
-                        return NSS_STATUS_NOTFOUND;
-                if (r < 0)
-                        goto fail;
-        } else {
-                r = sd_bus_call_method(bus,
-                                       "org.freedesktop.systemd1",
-                                       "/org/freedesktop/systemd1",
-                                       "org.freedesktop.systemd1.Manager",
-                                       "LookupDynamicUserByName",
-                                       &error,
-                                       &reply,
-                                       "s",
-                                       name);
-                if (r < 0) {
-                        if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+                if (streq(name, nobody_group.gr_name)) {
+                        if (!synthesize_nobody())
                                 return NSS_STATUS_NOTFOUND;
 
-                        goto fail;
+                        *gr = nobody_group;
+                        return NSS_STATUS_SUCCESS;
                 }
 
-                r = sd_bus_message_read(reply, "u", &translated);
-                if (r < 0)
-                        goto fail;
-        }
+        } else if (STR_IN_SET(name, root_group.gr_name, nobody_group.gr_name))
+                return NSS_STATUS_NOTFOUND;
 
-        l = sizeof(char*) + strlen(name) + 1;
-        if (buflen < l) {
+        status = userdb_getgrnam(name, gr, buffer, buflen, &e);
+        if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
                 UNPROTECT_ERRNO;
-                *errnop = ERANGE;
-                return NSS_STATUS_TRYAGAIN;
+                *errnop = e;
+                return status;
         }
 
-        memzero(buffer, sizeof(char*));
-        strcpy(buffer + sizeof(char*), name);
-
-        gr->gr_name = buffer + sizeof(char*);
-        gr->gr_gid = (gid_t) translated;
-        gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
-        gr->gr_mem = (char**) buffer;
-
-        return NSS_STATUS_SUCCESS;
-
-fail:
-        UNPROTECT_ERRNO;
-        *errnop = -r;
-        return NSS_STATUS_UNAVAIL;
+        return status;
 }
 
 enum nss_status _nss_systemd_getgrgid_r(
@@ -440,154 +230,56 @@ enum nss_status _nss_systemd_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;
-        _cleanup_free_ char *direct = NULL;
-        const char *translated;
-        size_t l;
-        int bypass, r;
+        enum nss_status status;
+        int e;
 
         PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
+        assert(gr);
+        assert(errnop);
+
         if (!gid_is_valid(gid))
                 return NSS_STATUS_NOTFOUND;
 
         /* Synthesize records for root and nobody, in case they are missing from /etc/group */
         if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
+
                 if (gid == root_group.gr_gid) {
                         *gr = root_group;
                         return NSS_STATUS_SUCCESS;
                 }
-                if (synthesize_nobody() &&
-                    gid == nobody_group.gr_gid) {
-                        *gr = nobody_group;
-                        return NSS_STATUS_SUCCESS;
-                }
-        }
-
-        if (!gid_is_dynamic(gid))
-                return NSS_STATUS_NOTFOUND;
-
-        if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
-                return NSS_STATUS_NOTFOUND;
-
-        bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
-        if (bypass <= 0) {
-                r = sd_bus_open_system(&bus);
-                if (r < 0)
-                        bypass = 1;
-        }
 
-        if (bypass > 0) {
-                r = direct_lookup_uid(gid, &direct);
-                if (r == -ENOENT)
-                        return NSS_STATUS_NOTFOUND;
-                if (r < 0)
-                        goto fail;
-
-                translated = direct;
-
-        } else {
-                r = sd_bus_call_method(bus,
-                                       "org.freedesktop.systemd1",
-                                       "/org/freedesktop/systemd1",
-                                       "org.freedesktop.systemd1.Manager",
-                                       "LookupDynamicUserByUID",
-                                       &error,
-                                       &reply,
-                                       "u",
-                                       (uint32_t) gid);
-                if (r < 0) {
-                        if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
+                if (gid == nobody_group.gr_gid) {
+                        if (!synthesize_nobody())
                                 return NSS_STATUS_NOTFOUND;
 
-                        goto fail;
+                        *gr = nobody_group;
+                        return NSS_STATUS_SUCCESS;
                 }
 
-                r = sd_bus_message_read(reply, "s", &translated);
-                if (r < 0)
-                        goto fail;
-        }
+        } else if (gid == root_group.gr_gid || gid == nobody_group.gr_gid)
+                return NSS_STATUS_NOTFOUND;
 
-        l = sizeof(char*) + strlen(translated) + 1;
-        if (buflen < l) {
+        status = userdb_getgrgid(gid, gr, buffer, buflen, &e);
+        if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
                 UNPROTECT_ERRNO;
-                *errnop = ERANGE;
-                return NSS_STATUS_TRYAGAIN;
+                *errnop = e;
+                return status;
         }
 
-        memzero(buffer, sizeof(char*));
-        strcpy(buffer + sizeof(char*), translated);
-
-        gr->gr_name = buffer + sizeof(char*);
-        gr->gr_gid = gid;
-        gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
-        gr->gr_mem = (char**) buffer;
-
-        return NSS_STATUS_SUCCESS;
-
-fail:
-        UNPROTECT_ERRNO;
-        *errnop = -r;
-        return NSS_STATUS_UNAVAIL;
-}
-
-static void user_entry_free(UserEntry *p) {
-        if (!p)
-                return;
-
-        if (p->data)
-                LIST_REMOVE(entries, p->data->entries, p);
-
-        free(p->name);
-        free(p);
-}
-
-static int user_entry_add(GetentData *data, const char *name, uid_t id) {
-        UserEntry *p;
-
-        assert(data);
-
-        /* This happens when User= or Group= already exists statically. */
-        if (!uid_is_dynamic(id))
-                return -EINVAL;
-
-        p = new0(UserEntry, 1);
-        if (!p)
-                return -ENOMEM;
-
-        p->name = strdup(name);
-        if (!p->name) {
-                free(p);
-                return -ENOMEM;
-        }
-        p->id = id;
-        p->data = data;
-
-        LIST_PREPEND(entries, data->entries, p);
-
-        return 0;
-}
-
-static void systemd_endent(GetentData *data) {
-        UserEntry *p;
-
-        assert(data);
-
-        while ((p = data->entries))
-                user_entry_free(p);
-
-        data->position = NULL;
+        return status;
 }
 
 static enum nss_status nss_systemd_endent(GetentData *p) {
         PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
+        assert(p);
+
         assert_se(pthread_mutex_lock(&p->mutex) == 0);
-        systemd_endent(p);
+        p->iterator = userdb_iterator_free(p->iterator);
+        p->by_membership = false;
         assert_se(pthread_mutex_unlock(&p->mutex) == 0);
 
         return NSS_STATUS_SUCCESS;
@@ -601,235 +293,353 @@ enum nss_status _nss_systemd_endgrent(void) {
         return nss_systemd_endent(&getgrent_data);
 }
 
-static int direct_enumeration(GetentData *p) {
-        _cleanup_closedir_ DIR *d = NULL;
-        struct dirent *de;
-        int r;
+enum nss_status _nss_systemd_setpwent(int stayopen) {
+        enum nss_status ret;
 
-        assert(p);
+        PROTECT_ERRNO;
+        BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
-        d = opendir("/run/systemd/dynamic-uid/");
-        if (!d)
-                return -errno;
+        if (userdb_nss_compat_is_enabled() <= 0)
+                return NSS_STATUS_NOTFOUND;
 
-        FOREACH_DIRENT(de, d, return -errno) {
-                _cleanup_free_ char *name = NULL;
-                uid_t uid, verified;
+        assert_se(pthread_mutex_lock(&getpwent_data.mutex) == 0);
 
-                if (!dirent_is_file(de))
-                        continue;
+        getpwent_data.iterator = userdb_iterator_free(getpwent_data.iterator);
+        getpwent_data.by_membership = false;
 
-                r = parse_uid(de->d_name, &uid);
-                if (r < 0)
-                        continue;
+        ret = userdb_all(nss_glue_userdb_flags(), &getpwent_data.iterator) < 0 ?
+                NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
 
-                r = direct_lookup_uid(uid, &name);
-                if (r == -ENOMEM)
-                        return r;
-                if (r < 0)
-                        continue;
+        assert_se(pthread_mutex_unlock(&getpwent_data.mutex) == 0);
+        return ret;
+}
 
-                r = direct_lookup_name(name, &verified);
-                if (r < 0)
-                        continue;
+enum nss_status _nss_systemd_setgrent(int stayopen) {
+        enum nss_status ret;
 
-                if (uid != verified)
-                        continue;
+        PROTECT_ERRNO;
+        BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
-                r = user_entry_add(p, name, uid);
-                if (r == -ENOMEM)
-                        return r;
-                if (r < 0)
-                        continue;
-        }
+        if (userdb_nss_compat_is_enabled() <= 0)
+                return NSS_STATUS_NOTFOUND;
 
-        return 0;
+        assert_se(pthread_mutex_lock(&getgrent_data.mutex) == 0);
+
+        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;
 }
 
-static enum nss_status systemd_setent(GetentData *p) {
-        _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 *name;
-        uid_t id;
-        int bypass, r;
+enum nss_status _nss_systemd_getpwent_r(
+                struct passwd *result,
+                char *buffer, size_t buflen,
+                int *errnop) {
+
+        _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+        enum nss_status ret;
+        int r;
 
         PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
-        assert(p);
+        assert(result);
+        assert(errnop);
 
-        assert_se(pthread_mutex_lock(&p->mutex) == 0);
+        r = userdb_nss_compat_is_enabled();
+        if (r < 0) {
+                UNPROTECT_ERRNO;
+                *errnop = -r;
+                return NSS_STATUS_UNAVAIL;
+        }
+        if (!r)
+                return NSS_STATUS_NOTFOUND;
 
-        systemd_endent(p);
+        assert_se(pthread_mutex_lock(&getpwent_data.mutex) == 0);
 
-        if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
+        if (!getpwent_data.iterator) {
+                UNPROTECT_ERRNO;
+                *errnop = EHOSTDOWN;
+                ret = NSS_STATUS_UNAVAIL;
                 goto finish;
-
-        bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
-
-        if (bypass <= 0) {
-                r = sd_bus_open_system(&bus);
-                if (r < 0)
-                        bypass = 1;
         }
 
-        if (bypass > 0) {
-                r = direct_enumeration(p);
-                if (r < 0)
-                        goto fail;
-
+        r = userdb_iterator_get(getpwent_data.iterator, &ur);
+        if (r == -ESRCH) {
+                ret = NSS_STATUS_NOTFOUND;
+                goto finish;
+        }
+        if (r < 0) {
+                UNPROTECT_ERRNO;
+                *errnop = -r;
+                ret = NSS_STATUS_UNAVAIL;
                 goto finish;
         }
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.systemd1",
-                               "/org/freedesktop/systemd1",
-                               "org.freedesktop.systemd1.Manager",
-                               "GetDynamicUsers",
-                               &error,
-                               &reply,
-                               NULL);
-        if (r < 0)
-                goto fail;
-
-        r = sd_bus_message_enter_container(reply, 'a', "(us)");
-        if (r < 0)
-                goto fail;
-
-        while ((r = sd_bus_message_read(reply, "(us)", &id, &name)) > 0) {
-                r = user_entry_add(p, name, id);
-                if (r == -ENOMEM)
-                        goto fail;
-                if (r < 0)
-                        continue;
+        r = nss_pack_user_record(ur, result, buffer, buflen);
+        if (r < 0) {
+                UNPROTECT_ERRNO;
+                *errnop = -r;
+                ret = NSS_STATUS_TRYAGAIN;
+                goto finish;
         }
-        if (r < 0)
-                goto fail;
 
-        r = sd_bus_message_exit_container(reply);
-        if (r < 0)
-                goto fail;
+        ret = NSS_STATUS_SUCCESS;
 
 finish:
-        p->position = p->entries;
-        assert_se(pthread_mutex_unlock(&p->mutex) == 0);
-
-        return NSS_STATUS_SUCCESS;
-
-fail:
-        systemd_endent(p);
-        assert_se(pthread_mutex_unlock(&p->mutex) == 0);
-
-        return NSS_STATUS_UNAVAIL;
-}
-
-enum nss_status _nss_systemd_setpwent(int stayopen) {
-        return systemd_setent(&getpwent_data);
+        assert_se(pthread_mutex_unlock(&getpwent_data.mutex) == 0);
+        return ret;
 }
 
-enum nss_status _nss_systemd_setgrent(int stayopen) {
-        return systemd_setent(&getgrent_data);
-}
+enum nss_status _nss_systemd_getgrent_r(
+                struct group *result,
+                char *buffer, size_t buflen,
+                int *errnop) {
 
-enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) {
+        _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
+        _cleanup_free_ char **members = NULL;
         enum nss_status ret;
-        UserEntry *p;
-        size_t len;
+        int r;
 
         PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
         assert(result);
-        assert(buffer);
         assert(errnop);
 
-        assert_se(pthread_mutex_lock(&getpwent_data.mutex) == 0);
+        r = userdb_nss_compat_is_enabled();
+        if (r < 0) {
+                UNPROTECT_ERRNO;
+                *errnop = -r;
+                return NSS_STATUS_UNAVAIL;
+        }
+        if (!r)
+                return NSS_STATUS_UNAVAIL;
 
-        LIST_FOREACH(entries, p, getpwent_data.position) {
-                len = strlen(p->name) + 1;
-                if (buflen < len) {
+        assert_se(pthread_mutex_lock(&getgrent_data.mutex) == 0);
+
+        if (!getgrent_data.iterator) {
+                UNPROTECT_ERRNO;
+                *errnop = EHOSTDOWN;
+                ret = NSS_STATUS_UNAVAIL;
+                goto finish;
+        }
+
+        if (!getgrent_data.by_membership) {
+                r = groupdb_iterator_get(getgrent_data.iterator, &gr);
+                if (r == -ESRCH) {
+                        /* So we finished iterating native groups now. let's now continue with iterating
+                         * native memberships, and generate additional group entries for any groups
+                         * referenced there that are defined in NSS only. This means for those groups there
+                         * will be two or more entries generated during iteration, but this is apparently how
+                         * this is supposed to work, and what other implementations do too. Clients are
+                         * supposed to merge the group records found during iteration automatically. */
+                        getgrent_data.iterator = userdb_iterator_free(getgrent_data.iterator);
+
+                        r = membershipdb_all(nss_glue_userdb_flags(), &getgrent_data.iterator);
+                        if (r < 0) {
+                                UNPROTECT_ERRNO;
+                                *errnop = -r;
+                                ret = NSS_STATUS_UNAVAIL;
+                                goto finish;
+                        }
+
+                        getgrent_data.by_membership = true;
+                } else if (r < 0) {
                         UNPROTECT_ERRNO;
-                        *errnop = ERANGE;
-                        ret = NSS_STATUS_TRYAGAIN;
-                        goto finalize;
+                        *errnop = -r;
+                        ret = NSS_STATUS_UNAVAIL;
+                        goto finish;
+                } 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;
+                        }
                 }
+        }
 
-                memcpy(buffer, p->name, len);
-
-                result->pw_name = buffer;
-                result->pw_uid = p->id;
-                result->pw_gid = p->id;
-                result->pw_gecos = (char*) DYNAMIC_USER_GECOS;
-                result->pw_passwd = (char*) DYNAMIC_USER_PASSWD;
-                result->pw_dir = (char*) DYNAMIC_USER_DIR;
-                result->pw_shell = (char*) DYNAMIC_USER_SHELL;
-                break;
+        if (getgrent_data.by_membership) {
+                _cleanup_close_ int lock_fd = -1;
+
+                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 < 0) {
+                                UNPROTECT_ERRNO;
+                                *errnop = -r;
+                                ret = NSS_STATUS_UNAVAIL;
+                                goto finish;
+                        }
+
+                        if (STR_IN_SET(user_name, root_passwd.pw_name, nobody_passwd.pw_name))
+                                continue;
+                        if (STR_IN_SET(group_name, root_group.gr_name, nobody_group.gr_name))
+                                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) {
+                                        UNPROTECT_ERRNO;
+                                        *errnop = -lock_fd;
+                                        ret = NSS_STATUS_UNAVAIL;
+                                        goto finish;
+                                }
+                        }
+
+                        r = nss_group_record_by_name(group_name, &gr);
+                        if (r == -ESRCH)
+                                continue;
+                        if (r < 0) {
+                                log_debug_errno(r, "Failed to do NSS check for group '%s', ignoring: %m", group_name);
+                                continue;
+                        }
+
+                        members = strv_new(user_name);
+                        if (!members) {
+                                UNPROTECT_ERRNO;
+                                *errnop = ENOMEM;
+                                return NSS_STATUS_TRYAGAIN;
+                        }
+
+                        /* Note that we currently generate one group entry per user that is part of a
+                         * group. It's a bit ugly, but equivalent to generating a single entry with a set of
+                         * members in them. */
+                        break;
+                }
         }
-        if (!p) {
-                ret = NSS_STATUS_NOTFOUND;
-                goto finalize;
+
+        r = nss_pack_group_record(gr, members, result, buffer, buflen);
+        if (r < 0) {
+                UNPROTECT_ERRNO;
+                *errnop = -r;
+                ret = NSS_STATUS_TRYAGAIN;
+                goto finish;
         }
 
-        /* On success, step to the next entry. */
-        p = p->entries_next;
         ret = NSS_STATUS_SUCCESS;
 
-finalize:
-        /* Save position for the next call. */
-        getpwent_data.position = p;
-
-        assert_se(pthread_mutex_unlock(&getpwent_data.mutex) == 0);
-
+finish:
+        assert_se(pthread_mutex_unlock(&getgrent_data.mutex) == 0);
         return ret;
 }
 
-enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size_t buflen, int *errnop) {
-        enum nss_status ret;
-        UserEntry *p;
-        size_t len;
+enum nss_status _nss_systemd_initgroups_dyn(
+                const char *user_name,
+                gid_t gid,
+                long *start,
+                long *size,
+                gid_t **groupsp,
+                long int limit,
+                int *errnop) {
+
+        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+        bool any = false;
+        int r;
 
         PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
-        assert(result);
-        assert(buffer);
+        assert(user_name);
+        assert(start);
+        assert(size);
+        assert(groupsp);
         assert(errnop);
 
-        assert_se(pthread_mutex_lock(&getgrent_data.mutex) == 0);
+        if (!valid_user_group_name(user_name))
+                return NSS_STATUS_NOTFOUND;
+
+        /* Don't allow extending these two special users, the same as we won't resolve them via getpwnam() */
+        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)
+                return NSS_STATUS_NOTFOUND;
+
+        r = membershipdb_by_user(user_name, nss_glue_userdb_flags(), &iterator);
+        if (r < 0) {
+                UNPROTECT_ERRNO;
+                *errnop = -r;
+                return NSS_STATUS_UNAVAIL;
+        }
 
-        LIST_FOREACH(entries, p, getgrent_data.position) {
-                len = sizeof(char*) + strlen(p->name) + 1;
-                if (buflen < len) {
+        for (;;) {
+                _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
+                _cleanup_free_ char *group_name = NULL;
+
+                r = membershipdb_iterator_get(iterator, NULL, &group_name);
+                if (r == -ESRCH)
+                        break;
+                if (r < 0) {
                         UNPROTECT_ERRNO;
-                        *errnop = ERANGE;
-                        ret = NSS_STATUS_TRYAGAIN;
-                        goto finalize;
+                        *errnop = -r;
+                        return NSS_STATUS_UNAVAIL;
                 }
 
-                memzero(buffer, sizeof(char*));
-                strcpy(buffer + sizeof(char*), p->name);
+                /* 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. */
 
-                result->gr_name = buffer + sizeof(char*);
-                result->gr_gid = p->id;
-                result->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
-                result->gr_mem = (char**) buffer;
-                break;
-        }
-        if (!p) {
-                ret = NSS_STATUS_NOTFOUND;
-                goto finalize;
-        }
+                r = groupdb_by_name(group_name, nss_glue_userdb_flags() & ~USERDB_AVOID_NSS, &g);
+                if (r == -ESRCH)
+                        continue;
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to resolve group '%s', ignoring: %m", group_name);
+                        continue;
+                }
 
-        /* On success, step to the next entry. */
-        p = p->entries_next;
-        ret = NSS_STATUS_SUCCESS;
+                if (g->gid == gid)
+                        continue;
 
-finalize:
-        /* Save position for the next call. */
-        getgrent_data.position = p;
+                if (*start >= *size) {
+                        gid_t *new_groups;
+                        long new_size;
+
+                        if (limit > 0 && *size >= limit) /* Reached the limit.? */
+                                break;
+
+                        if (*size > LONG_MAX/2) { /* Check for overflow */
+                                UNPROTECT_ERRNO;
+                                *errnop = ENOMEM;
+                                return NSS_STATUS_TRYAGAIN;
+                        }
+
+                        new_size = *start * 2;
+                        if (limit > 0 && new_size > limit)
+                                new_size = limit;
+
+                        /* Enlarge buffer */
+                        new_groups = realloc(*groupsp, new_size * sizeof(**groupsp));
+                        if (!new_groups) {
+                                UNPROTECT_ERRNO;
+                                *errnop = ENOMEM;
+                                return NSS_STATUS_TRYAGAIN;
+                        }
+
+                        *groupsp = new_groups;
+                        *size = new_size;
+                }
 
-        assert_se(pthread_mutex_unlock(&getgrent_data.mutex) == 0);
+                (*groupsp)[(*start)++] = g->gid;
+                any = true;
+        }
 
-        return ret;
+        return any ? NSS_STATUS_SUCCESS : NSS_STATUS_NOTFOUND;
 }
index ff63382b152c901ebbed96d5191c4cd9fff3f484..77e1fbe93f227e6e878c7847bb9f773b9dfa7769 100644 (file)
@@ -19,5 +19,6 @@ global:
         _nss_systemd_endgrent;
         _nss_systemd_setgrent;
         _nss_systemd_getgrent_r;
+        _nss_systemd_initgroups_dyn;
 local: *;
 };
diff --git a/src/nss-systemd/userdb-glue.c b/src/nss-systemd/userdb-glue.c
new file mode 100644 (file)
index 0000000..58915c3
--- /dev/null
@@ -0,0 +1,344 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "env-util.h"
+#include "fd-util.h"
+#include "group-record-nss.h"
+#include "strv.h"
+#include "user-record.h"
+#include "userdb-glue.h"
+#include "userdb.h"
+
+UserDBFlags nss_glue_userdb_flags(void) {
+        UserDBFlags flags = USERDB_AVOID_NSS;
+
+        /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
+        if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
+                flags |= USERDB_AVOID_DYNAMIC_USER;
+
+        return flags;
+}
+
+int nss_pack_user_record(
+                UserRecord *hr,
+                struct passwd *pwd,
+                char *buffer,
+                size_t buflen) {
+
+        const char *rn, *hd, *shell;
+        size_t required;
+
+        assert(hr);
+        assert(pwd);
+
+        assert_se(hr->user_name);
+        required = strlen(hr->user_name) + 1;
+
+        assert_se(rn = user_record_real_name(hr));
+        required += strlen(rn) + 1;
+
+        assert_se(hd = user_record_home_directory(hr));
+        required += strlen(hd) + 1;
+
+        assert_se(shell = user_record_shell(hr));
+        required += strlen(shell) + 1;
+
+        if (buflen < required)
+                return -ERANGE;
+
+        *pwd = (struct passwd) {
+                .pw_name = buffer,
+                .pw_uid = hr->uid,
+                .pw_gid = user_record_gid(hr),
+                .pw_passwd = (char*) "x", /* means: see shadow file */
+        };
+
+        assert(buffer);
+
+        pwd->pw_gecos = stpcpy(pwd->pw_name, hr->user_name) + 1;
+        pwd->pw_dir = stpcpy(pwd->pw_gecos, rn) + 1;
+        pwd->pw_shell = stpcpy(pwd->pw_dir, hd) + 1;
+        strcpy(pwd->pw_shell, shell);
+
+        return 0;
+}
+
+enum nss_status userdb_getpwnam(
+                const char *name,
+                struct passwd *pwd,
+                char *buffer, size_t buflen,
+                int *errnop) {
+
+        _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+        int r;
+
+        assert(pwd);
+        assert(errnop);
+
+        r = userdb_nss_compat_is_enabled();
+        if (r < 0) {
+                *errnop = -r;
+                return NSS_STATUS_UNAVAIL;
+        }
+        if (!r)
+                return NSS_STATUS_NOTFOUND;
+
+        r = userdb_by_name(name, nss_glue_userdb_flags(), &hr);
+        if (r == -ESRCH)
+                return NSS_STATUS_NOTFOUND;
+        if (r < 0) {
+                *errnop = -r;
+                return NSS_STATUS_UNAVAIL;
+        }
+
+        r = nss_pack_user_record(hr, pwd, buffer, buflen);
+        if (r < 0) {
+                *errnop = -r;
+                return NSS_STATUS_TRYAGAIN;
+        }
+
+        return NSS_STATUS_SUCCESS;
+}
+
+enum nss_status userdb_getpwuid(
+                uid_t uid,
+                struct passwd *pwd,
+                char *buffer,
+                size_t buflen,
+                int *errnop) {
+
+        _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+        int r;
+
+        assert(pwd);
+        assert(errnop);
+
+        r = userdb_nss_compat_is_enabled();
+        if (r < 0) {
+                *errnop = -r;
+                return NSS_STATUS_UNAVAIL;
+        }
+        if (!r)
+                return NSS_STATUS_NOTFOUND;
+
+        r = userdb_by_uid(uid, nss_glue_userdb_flags(), &hr);
+        if (r == -ESRCH)
+                return NSS_STATUS_NOTFOUND;
+        if (r < 0) {
+                *errnop = -r;
+                return NSS_STATUS_UNAVAIL;
+        }
+
+        r = nss_pack_user_record(hr, pwd, buffer, buflen);
+        if (r < 0) {
+                *errnop = -r;
+                return NSS_STATUS_TRYAGAIN;
+        }
+
+        return NSS_STATUS_SUCCESS;
+}
+
+int nss_pack_group_record(
+                GroupRecord *g,
+                char **extra_members,
+                struct group *gr,
+                char *buffer,
+                size_t buflen) {
+
+        char **array = NULL, *p, **m;
+        size_t required, n = 0, i = 0;
+
+        assert(g);
+        assert(gr);
+
+        assert_se(g->group_name);
+        required = strlen(g->group_name) + 1;
+
+        STRV_FOREACH(m, g->members) {
+                required += sizeof(char*);  /* space for ptr array entry */
+                required += strlen(*m) + 1;
+                n++;
+        }
+        STRV_FOREACH(m, extra_members) {
+                if (strv_contains(g->members, *m))
+                        continue;
+
+                required += sizeof(char*);
+                required += strlen(*m) + 1;
+                n++;
+        }
+
+        required += sizeof(char*); /* trailing NULL in ptr array entry */
+
+        if (buflen < required)
+                return -ERANGE;
+
+        array = (char**) buffer; /* place ptr array at beginning of buffer, under assumption buffer is aligned */
+        p = buffer + sizeof(void*) * (n + 1); /* place member strings right after the ptr array */
+
+        STRV_FOREACH(m, g->members) {
+                array[i++] = p;
+                p = stpcpy(p, *m) + 1;
+        }
+        STRV_FOREACH(m, extra_members) {
+                if (strv_contains(g->members, *m))
+                        continue;
+
+                array[i++] = p;
+                p = stpcpy(p, *m) + 1;
+        }
+
+        assert_se(i == n);
+        array[n] = NULL;
+
+        *gr = (struct group) {
+                .gr_name = strcpy(p, g->group_name),
+                .gr_gid = g->gid,
+                .gr_passwd = (char*) "x", /* means: see shadow file */
+                .gr_mem = array,
+        };
+
+        return 0;
+}
+
+enum nss_status userdb_getgrnam(
+                const char *name,
+                struct group *gr,
+                char *buffer,
+                size_t buflen,
+                int *errnop) {
+
+        _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
+        _cleanup_strv_free_ char **members = NULL;
+        int r;
+
+        assert(gr);
+        assert(errnop);
+
+        r = userdb_nss_compat_is_enabled();
+        if (r < 0) {
+                *errnop = -r;
+                return NSS_STATUS_UNAVAIL;
+        }
+        if (!r)
+                return NSS_STATUS_NOTFOUND;
+
+        r = groupdb_by_name(name, nss_glue_userdb_flags(), &g);
+        if (r < 0 && r != -ESRCH) {
+                *errnop = -r;
+                return NSS_STATUS_UNAVAIL;
+        }
+
+        r = membershipdb_by_group_strv(name, nss_glue_userdb_flags(), &members);
+        if (r < 0) {
+                *errnop = -r;
+                return NSS_STATUS_UNAVAIL;
+        }
+
+        if (!g) {
+                _cleanup_close_ int lock_fd = -1;
+
+                if (strv_isempty(members))
+                        return NSS_STATUS_NOTFOUND;
+
+                /* Grmbl, so we are supposed to extend a group entry, but the group entry itself is not
+                 * accessible via non-NSS. Hence let's do what we have to do, and query NSS after all to
+                 * 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. */
+
+                lock_fd = userdb_nss_compat_disable();
+                if (lock_fd < 0 && lock_fd != -EBUSY)
+                        return lock_fd;
+
+                r = nss_group_record_by_name(name, &g);
+                if (r == -ESRCH)
+                        return NSS_STATUS_NOTFOUND;
+                if (r < 0) {
+                        *errnop = -r;
+                        return NSS_STATUS_UNAVAIL;
+                }
+        }
+
+        r = nss_pack_group_record(g, members, gr, buffer, buflen);
+        if (r < 0) {
+                *errnop = -r;
+                return NSS_STATUS_TRYAGAIN;
+        }
+
+        return NSS_STATUS_SUCCESS;
+}
+
+enum nss_status userdb_getgrgid(
+                gid_t gid,
+                struct group *gr,
+                char *buffer,
+                size_t buflen,
+                int *errnop) {
+
+
+        _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
+        _cleanup_strv_free_ char **members = NULL;
+        bool from_nss;
+        int r;
+
+        assert(gr);
+        assert(errnop);
+
+        r = userdb_nss_compat_is_enabled();
+        if (r < 0) {
+                *errnop = -r;
+                return NSS_STATUS_UNAVAIL;
+        }
+        if (!r)
+                return NSS_STATUS_NOTFOUND;
+
+        r = groupdb_by_gid(gid, nss_glue_userdb_flags(), &g);
+        if (r < 0 && r != -ESRCH) {
+                *errnop = -r;
+                return NSS_STATUS_UNAVAIL;
+        }
+
+        if (!g) {
+                _cleanup_close_ int lock_fd = -1;
+
+                /* 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_group_record_by_gid(gid, &g);
+                if (r == -ESRCH)
+                        return NSS_STATUS_NOTFOUND;
+
+                if (r < 0) {
+                        *errnop = -r;
+                        return NSS_STATUS_UNAVAIL;
+                }
+
+                from_nss = true;
+        } else
+                from_nss = false;
+
+        r = membershipdb_by_group_strv(g->group_name, nss_glue_userdb_flags(), &members);
+        if (r < 0) {
+                *errnop = -r;
+                return NSS_STATUS_UNAVAIL;
+        }
+
+        /* If we acquired the record via NSS then there's no reason to respond unless we have to agument the
+         * list of members of the group */
+        if (from_nss && strv_isempty(members))
+                return NSS_STATUS_NOTFOUND;
+
+        r = nss_pack_group_record(g, members, gr, buffer, buflen);
+        if (r < 0) {
+                *errnop = -r;
+                return NSS_STATUS_TRYAGAIN;
+        }
+
+        return NSS_STATUS_SUCCESS;
+}
diff --git a/src/nss-systemd/userdb-glue.h b/src/nss-systemd/userdb-glue.h
new file mode 100644 (file)
index 0000000..02add24
--- /dev/null
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <nss.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/types.h>
+
+#include "userdb.h"
+
+UserDBFlags nss_glue_userdb_flags(void);
+
+int nss_pack_user_record(UserRecord *hr, struct passwd *pwd, char *buffer, size_t buflen);
+int nss_pack_group_record(GroupRecord *g, char **extra_members, struct group *gr, char *buffer, size_t buflen);
+
+enum nss_status userdb_getpwnam(const char *name, struct passwd *pwd, char *buffer, size_t buflen, int *errnop);
+enum nss_status userdb_getpwuid(uid_t uid, struct passwd *pwd, char *buffer, size_t buflen, int *errnop);
+
+enum nss_status userdb_getgrnam(const char *name, struct group *gr, char *buffer, size_t buflen, int *errnop);
+enum nss_status userdb_getgrgid(gid_t gid, struct group *gr, char *buffer, size_t buflen, int *errnop);
index ee4907f73f6249a5621baf613254cc820893a299..d73d67c4e8a4d80e457af13794630c84af0e3970 100644 (file)
@@ -1,6 +1,4 @@
-/***
-  SPDX-License-Identifier: LGPL-2.1+
-***/
+/* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include <fcntl.h>
 #include <sys/prctl.h>
@@ -43,8 +41,7 @@ static int makefs(const char *type, const char *device) {
 }
 
 static int run(int argc, char *argv[]) {
-        const char *device, *type;
-        _cleanup_free_ char *detected = NULL;
+        _cleanup_free_ char *device = NULL, *type = NULL, *detected = NULL;
         struct stat st;
         int r;
 
@@ -54,8 +51,14 @@ static int run(int argc, char *argv[]) {
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "This program expects two arguments.");
 
-        type = argv[1];
-        device = argv[2];
+        /* type and device must be copied because makefs calls safe_fork, which clears argv[] */
+        type = strdup(argv[1]);
+        if (!type)
+                return -ENOMEM;
+
+        device = strdup(argv[2]);
+        if (!device)
+                return -ENOMEM;
 
         if (stat(device, &st) < 0)
                 return log_error_errno(errno, "Failed to stat \"%s\": %m", device);
diff --git a/src/partition/meson.build b/src/partition/meson.build
new file mode 100644 (file)
index 0000000..d0c111a
--- /dev/null
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: LGPL-2.1+
+
+systemd_repart_sources = files('''
+        repart.c
+'''.split())
diff --git a/src/partition/repart.c b/src/partition/repart.c
new file mode 100644 (file)
index 0000000..3e52f26
--- /dev/null
@@ -0,0 +1,3029 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#if HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <libfdisk.h>
+#include <linux/fs.h>
+#include <linux/loop.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include <openssl/hmac.h>
+#include <openssl/sha.h>
+
+#include "sd-id128.h"
+
+#include "alloc-util.h"
+#include "blkid-util.h"
+#include "blockdev-util.h"
+#include "btrfs-util.h"
+#include "conf-files.h"
+#include "conf-parser.h"
+#include "def.h"
+#include "efivars.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "format-table.h"
+#include "format-util.h"
+#include "fs-util.h"
+#include "gpt.h"
+#include "id128-util.h"
+#include "list.h"
+#include "locale-util.h"
+#include "main-func.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "pretty-print.h"
+#include "proc-cmdline.h"
+#include "sort-util.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "utf8.h"
+
+/* Note: When growing and placing new partitions we always align to 4K sector size. It's how newer hard disks
+ * are designed, and if everything is aligned to that performance is best. And for older hard disks with 512B
+ * sector size devices were generally assumed to have an even number of sectors, hence at the worst we'll
+ * waste 3K per partition, which is probably fine. */
+
+static enum {
+        EMPTY_REFUSE,   /* refuse empty disks, never create a partition table */
+        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 */
+} arg_empty = EMPTY_REFUSE;
+
+static bool arg_dry_run = true;
+static const char *arg_node = NULL;
+static char *arg_root = NULL;
+static char *arg_definitions = NULL;
+static bool arg_discard = true;
+static bool arg_can_factory_reset = false;
+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_DESTRUCTOR_REGISTER(arg_root, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_definitions, freep);
+
+typedef struct Partition Partition;
+typedef struct FreeArea FreeArea;
+typedef struct Context Context;
+
+struct Partition {
+        char *definition_path;
+
+        sd_id128_t type_uuid;
+        sd_id128_t current_uuid, new_uuid;
+        char *current_label, *new_label;
+
+        bool dropped;
+        bool factory_reset;
+        int32_t priority;
+
+        uint32_t weight, padding_weight;
+
+        uint64_t current_size, new_size;
+        uint64_t size_min, size_max;
+
+        uint64_t current_padding, new_padding;
+        uint64_t padding_min, padding_max;
+
+        uint64_t partno;
+        uint64_t offset;
+
+        struct fdisk_partition *current_partition;
+        struct fdisk_partition *new_partition;
+        FreeArea *padding_area;
+        FreeArea *allocated_to_area;
+
+        LIST_FIELDS(Partition, partitions);
+};
+
+#define PARTITION_IS_FOREIGN(p) (!(p)->definition_path)
+#define PARTITION_EXISTS(p) (!!(p)->current_partition)
+
+struct FreeArea {
+        Partition *after;
+        uint64_t size;
+        uint64_t allocated;
+};
+
+struct Context {
+        LIST_HEAD(Partition, partitions);
+        size_t n_partitions;
+
+        FreeArea **free_areas;
+        size_t n_free_areas, n_allocated_free_areas;
+
+        uint64_t start, end, total;
+
+        struct fdisk_context *fdisk_context;
+
+        sd_id128_t seed;
+};
+
+static uint64_t round_down_size(uint64_t v, uint64_t p) {
+        return (v / p) * p;
+}
+
+static uint64_t round_up_size(uint64_t v, uint64_t p) {
+
+        v = DIV_ROUND_UP(v, p);
+
+        if (v > UINT64_MAX / p)
+                return UINT64_MAX; /* overflow */
+
+        return v * p;
+}
+
+static Partition *partition_new(void) {
+        Partition *p;
+
+        p = new(Partition, 1);
+        if (!p)
+                return NULL;
+
+        *p = (Partition) {
+                .weight = 1000,
+                .padding_weight = 0,
+                .current_size = UINT64_MAX,
+                .new_size = UINT64_MAX,
+                .size_min = UINT64_MAX,
+                .size_max = UINT64_MAX,
+                .current_padding = UINT64_MAX,
+                .new_padding = UINT64_MAX,
+                .padding_min = UINT64_MAX,
+                .padding_max = UINT64_MAX,
+                .partno = UINT64_MAX,
+                .offset = UINT64_MAX,
+        };
+
+        return p;
+}
+
+static Partition* partition_free(Partition *p) {
+        if (!p)
+                return NULL;
+
+        free(p->current_label);
+        free(p->new_label);
+        free(p->definition_path);
+
+        if (p->current_partition)
+                fdisk_unref_partition(p->current_partition);
+        if (p->new_partition)
+                fdisk_unref_partition(p->new_partition);
+
+        return mfree(p);
+}
+
+static Partition* partition_unlink_and_free(Context *context, Partition *p) {
+        if (!p)
+                return NULL;
+
+        LIST_REMOVE(partitions, context->partitions, p);
+
+        assert(context->n_partitions > 0);
+        context->n_partitions--;
+
+        return partition_free(p);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Partition*, partition_free);
+
+static Context *context_new(sd_id128_t seed) {
+        Context *context;
+
+        context = new(Context, 1);
+        if (!context)
+                return NULL;
+
+        *context = (Context) {
+                .start = UINT64_MAX,
+                .end = UINT64_MAX,
+                .total = UINT64_MAX,
+                .seed = seed,
+        };
+
+        return context;
+}
+
+static void context_free_free_areas(Context *context) {
+        assert(context);
+
+        for (size_t i = 0; i < context->n_free_areas; i++)
+                free(context->free_areas[i]);
+
+        context->free_areas = mfree(context->free_areas);
+        context->n_free_areas = 0;
+        context->n_allocated_free_areas = 0;
+}
+
+static Context *context_free(Context *context) {
+        if (!context)
+                return NULL;
+
+        while (context->partitions)
+                partition_unlink_and_free(context, context->partitions);
+        assert(context->n_partitions == 0);
+
+        context_free_free_areas(context);
+
+        if (context->fdisk_context)
+                fdisk_unref_context(context->fdisk_context);
+
+        return mfree(context);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Context*, context_free);
+
+static int context_add_free_area(
+                Context *context,
+                uint64_t size,
+                Partition *after) {
+
+        FreeArea *a;
+
+        assert(context);
+        assert(!after || !after->padding_area);
+
+        if (!GREEDY_REALLOC(context->free_areas, context->n_allocated_free_areas, context->n_free_areas + 1))
+                return -ENOMEM;
+
+        a = new(FreeArea, 1);
+        if (!a)
+                return -ENOMEM;
+
+        *a = (FreeArea) {
+                .size = size,
+                .after = after,
+        };
+
+        context->free_areas[context->n_free_areas++] = a;
+
+        if (after)
+                after->padding_area = a;
+
+        return 0;
+}
+
+static bool context_drop_one_priority(Context *context) {
+        int32_t priority = 0;
+        Partition *p;
+        bool exists = false;
+
+        LIST_FOREACH(partitions, p, context->partitions) {
+                if (p->dropped)
+                        continue;
+                if (p->priority < priority)
+                        continue;
+                if (p->priority == priority) {
+                        exists = exists || PARTITION_EXISTS(p);
+                        continue;
+                }
+
+                priority = p->priority;
+                exists = PARTITION_EXISTS(p);
+        }
+
+        /* Refuse to drop partitions with 0 or negative priorities or partitions of priorities that have at
+         * least one existing priority */
+        if (priority <= 0 || exists)
+                return false;
+
+        LIST_FOREACH(partitions, p, context->partitions) {
+                if (p->priority < priority)
+                        continue;
+
+                if (p->dropped)
+                        continue;
+
+                p->dropped = true;
+                log_info("Can't fit partition %s of priority %" PRIi32 ", dropping.", p->definition_path, p->priority);
+        }
+
+        return true;
+}
+
+static uint64_t partition_min_size(const Partition *p) {
+        uint64_t sz;
+
+        /* Calculate the disk space we really need at minimum for this partition. If the partition already
+         * exists the current size is what we really need. If it doesn't exist yet refuse to allocate less
+         * than 4K. */
+
+        if (PARTITION_IS_FOREIGN(p)) {
+                /* Don't allow changing size of partitions not managed by us */
+                assert(p->current_size != UINT64_MAX);
+                return p->current_size;
+        }
+
+        sz = p->current_size != UINT64_MAX ? p->current_size : 4096;
+        if (p->size_min != UINT64_MAX)
+                return MAX(p->size_min, sz);
+
+        return sz;
+}
+
+static uint64_t partition_max_size(const Partition *p) {
+        /* Calculate how large the partition may become at max. This is generally the configured maximum
+         * size, except when it already exists and is larger than that. In that case it's the existing size,
+         * since we never want to shrink partitions. */
+
+        if (PARTITION_IS_FOREIGN(p)) {
+                /* Don't allow changing size of partitions not managed by us */
+                assert(p->current_size != UINT64_MAX);
+                return p->current_size;
+        }
+
+        if (p->current_size != UINT64_MAX)
+                return MAX(p->current_size, p->size_max);
+
+        return p->size_max;
+}
+
+static uint64_t partition_min_size_with_padding(const Partition *p) {
+        uint64_t sz;
+
+        /* Calculate the disk space we need for this partition plus any free space coming after it. This
+         * takes user configured padding into account as well as any additional whitespace needed to align
+         * the next partition to 4K again. */
+
+        sz = partition_min_size(p);
+
+        if (p->padding_min != UINT64_MAX)
+                sz += p->padding_min;
+
+        if (PARTITION_EXISTS(p)) {
+                /* If the partition wasn't aligned, add extra space so that any we might add will be aligned */
+                assert(p->offset != UINT64_MAX);
+                return round_up_size(p->offset + sz, 4096) - p->offset;
+        }
+
+        /* If this is a new partition we'll place it aligned, hence we just need to round up the required size here */
+        return round_up_size(sz, 4096);
+}
+
+static uint64_t free_area_available(const FreeArea *a) {
+        assert(a);
+
+        /* Determines how much of this free area is not allocated yet */
+
+        assert(a->size >= a->allocated);
+        return a->size - a->allocated;
+}
+
+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. */
+
+        avail = free_area_available(a);
+        if (a->after) {
+                uint64_t need, space;
+
+                need = partition_min_size_with_padding(a->after);
+
+                assert(a->after->offset != UINT64_MAX);
+                assert(a->after->current_size != UINT64_MAX);
+
+                space = round_up_size(a->after->offset + a->after->current_size, 4096) - a->after->offset + avail;
+                if (need >= space)
+                        return 0;
+
+                return space - need;
+        }
+
+        return avail;
+}
+
+static int free_area_compare(FreeArea *const *a, FreeArea *const*b) {
+        return CMP(free_area_available_for_new_partitions(*a),
+                   free_area_available_for_new_partitions(*b));
+}
+
+static uint64_t charge_size(uint64_t total, uint64_t amount) {
+        uint64_t rounded;
+
+        assert(amount <= total);
+
+        /* Subtract the specified amount from total, rounding up to multiple of 4K if there's room */
+        rounded = round_up_size(amount, 4096);
+        if (rounded >= total)
+                return 0;
+
+        return total - rounded;
+}
+
+static uint64_t charge_weight(uint64_t total, uint64_t amount) {
+        assert(amount <= total);
+        return total - amount;
+}
+
+static bool context_allocate_partitions(Context *context) {
+        Partition *p;
+
+        assert(context);
+
+        /* A simple first-fit algorithm, assuming the array of free areas is sorted by size in decreasing
+         * order. */
+
+        LIST_FOREACH(partitions, p, context->partitions) {
+                bool fits = false;
+                uint64_t required;
+                FreeArea *a = NULL;
+
+                /* Skip partitions we already dropped or that already exist */
+                if (p->dropped || PARTITION_EXISTS(p))
+                        continue;
+
+                /* Sort by size */
+                typesafe_qsort(context->free_areas, context->n_free_areas, free_area_compare);
+
+                /* How much do we need to fit? */
+                required = partition_min_size_with_padding(p);
+                assert(required % 4096 == 0);
+
+                for (size_t i = 0; i < context->n_free_areas; i++) {
+                        a = context->free_areas[i];
+
+                        if (free_area_available_for_new_partitions(a) >= required) {
+                                fits = true;
+                                break;
+                        }
+                }
+
+                if (!fits)
+                        return false; /* 😢 Oh no! We can't fit this partition into any free area! */
+
+                /* Assign the partition to this free area */
+                p->allocated_to_area = a;
+
+                /* Budget the minimal partition size */
+                a->allocated += required;
+        }
+
+        return true;
+}
+
+static int context_sum_weights(Context *context, FreeArea *a, uint64_t *ret) {
+        uint64_t weight_sum = 0;
+        Partition *p;
+
+        assert(context);
+        assert(a);
+        assert(ret);
+
+        /* Determine the sum of the weights of all partitions placed in or before the specified free area */
+
+        LIST_FOREACH(partitions, p, context->partitions) {
+                if (p->padding_area != a && p->allocated_to_area != a)
+                        continue;
+
+                if (p->weight > UINT64_MAX - weight_sum)
+                        goto overflow_sum;
+                weight_sum += p->weight;
+
+                if (p->padding_weight > UINT64_MAX - weight_sum)
+                        goto overflow_sum;
+                weight_sum += p->padding_weight;
+        }
+
+        *ret = weight_sum;
+        return 0;
+
+overflow_sum:
+        return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "Combined weight of partition exceeds unsigned 64bit range, refusing.");
+}
+
+static int scale_by_weight(uint64_t value, uint64_t weight, uint64_t weight_sum, uint64_t *ret) {
+        assert(weight_sum >= weight);
+        assert(ret);
+
+        if (weight == 0) {
+                *ret = 0;
+                return 0;
+        }
+
+        if (value > UINT64_MAX / weight)
+                return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "Scaling by weight of partition exceeds unsigned 64bit range, refusing.");
+
+        *ret = value * weight / weight_sum;
+        return 0;
+}
+
+typedef enum GrowPartitionPhase {
+        /* The first phase: we charge partitions which need more (according to constraints) than their weight-based share. */
+        PHASE_OVERCHARGE,
+
+        /* The second phase: we charge partitions which need less (according to constraints) than their weight-based share. */
+        PHASE_UNDERCHARGE,
+
+        /* The third phase: we distribute what remains among the remaining partitions, according to the weights */
+        PHASE_DISTRIBUTE,
+} GrowPartitionPhase;
+
+static int context_grow_partitions_phase(
+                Context *context,
+                FreeArea *a,
+                GrowPartitionPhase phase,
+                uint64_t *span,
+                uint64_t *weight_sum) {
+
+        Partition *p;
+        int r;
+
+        assert(context);
+        assert(a);
+
+        /* Now let's look at the intended weights and adjust them taking the minimum space assignments into
+         * account. i.e. if a partition has a small weight but a high minimum space value set it should not
+         * get any additional room from the left-overs. Similar, if two partitions have the same weight they
+         * should get the same space if possible, even if one has a smaller minimum size than the other. */
+        LIST_FOREACH(partitions, p, context->partitions) {
+
+                /* Look only at partitions associated with this free area, i.e. immediately
+                 * preceeding it, or allocated into it */
+                if (p->allocated_to_area != a && p->padding_area != a)
+                        continue;
+
+                if (p->new_size == UINT64_MAX) {
+                        bool charge = false, try_again = false;
+                        uint64_t share, rsz, xsz;
+
+                        /* Calculate how much this space this partition needs if everyone would get
+                         * the weight based share */
+                        r = scale_by_weight(*span, p->weight, *weight_sum, &share);
+                        if (r < 0)
+                                return r;
+
+                        rsz = partition_min_size(p);
+                        xsz = partition_max_size(p);
+
+                        if (phase == PHASE_OVERCHARGE && rsz > share) {
+                                /* This partition needs more than its calculated share. Let's assign
+                                 * it that, and take this partition out of all calculations and start
+                                 * again. */
+
+                                p->new_size = rsz;
+                                charge = try_again = true;
+
+                        } else if (phase == PHASE_UNDERCHARGE && xsz != UINT64_MAX && xsz < share) {
+                                /* This partition accepts less than its calculated
+                                 * share. Let's assign it that, and take this partition out
+                                 * of all calculations and start again. */
+
+                                p->new_size = xsz;
+                                charge = try_again = true;
+
+                        } else if (phase == PHASE_DISTRIBUTE) {
+                                /* This partition can accept its calculated share. Let's
+                                 * assign it. There's no need to restart things here since
+                                 * assigning this shouldn't impact the shares of the other
+                                 * partitions. */
+
+                                if (PARTITION_IS_FOREIGN(p))
+                                        /* Never change of foreign partitions (i.e. those we don't manage) */
+                                        p->new_size = p->current_size;
+                                else
+                                        p->new_size = MAX(round_down_size(share, 4096), rsz);
+
+                                charge = true;
+                        }
+
+                        if (charge) {
+                                *span = charge_size(*span, p->new_size);
+                                *weight_sum = charge_weight(*weight_sum, p->weight);
+                        }
+
+                        if (try_again)
+                                return 0; /* try again */
+                }
+
+                if (p->new_padding == UINT64_MAX) {
+                        bool charge = false, try_again = false;
+                        uint64_t share;
+
+                        r = scale_by_weight(*span, p->padding_weight, *weight_sum, &share);
+                        if (r < 0)
+                                return r;
+
+                        if (phase == PHASE_OVERCHARGE && p->padding_min != UINT64_MAX && p->padding_min > share) {
+                                p->new_padding = p->padding_min;
+                                charge = try_again = true;
+                        } else if (phase == PHASE_UNDERCHARGE && p->padding_max != UINT64_MAX && p->padding_max < share) {
+                                p->new_padding = p->padding_max;
+                                charge = try_again = true;
+                        } else if (phase == PHASE_DISTRIBUTE) {
+
+                                p->new_padding = round_down_size(share, 4096);
+                                if (p->padding_min != UINT64_MAX && p->new_padding < p->padding_min)
+                                        p->new_padding = p->padding_min;
+
+                                charge = true;
+                        }
+
+                        if (charge) {
+                                *span = charge_size(*span, p->new_padding);
+                                *weight_sum = charge_weight(*weight_sum, p->padding_weight);
+                        }
+
+                        if (try_again)
+                                return 0; /* try again */
+                }
+        }
+
+        return 1; /* done */
+}
+
+static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) {
+        uint64_t weight_sum = 0, span;
+        int r;
+
+        assert(context);
+        assert(a);
+
+        r = context_sum_weights(context, a, &weight_sum);
+        if (r < 0)
+                return r;
+
+        /* Let's calculate the total area covered by this free area and the partition before it */
+        span = a->size;
+        if (a->after) {
+                assert(a->after->offset != UINT64_MAX);
+                assert(a->after->current_size != UINT64_MAX);
+
+                span += round_up_size(a->after->offset + a->after->current_size, 4096) - a->after->offset;
+        }
+
+        GrowPartitionPhase phase = PHASE_OVERCHARGE;
+        for (;;) {
+                r = context_grow_partitions_phase(context, a, phase, &span, &weight_sum);
+                if (r < 0)
+                        return r;
+                if (r == 0) /* not done yet, re-run this phase */
+                        continue;
+
+                if (phase == PHASE_OVERCHARGE)
+                        phase = PHASE_UNDERCHARGE;
+                else if (phase == PHASE_UNDERCHARGE)
+                        phase = PHASE_DISTRIBUTE;
+                else if (phase == PHASE_DISTRIBUTE)
+                        break;
+        }
+
+        /* We still have space left over? Donate to preceeding partition if we have one */
+        if (span > 0 && a->after && !PARTITION_IS_FOREIGN(a->after)) {
+                uint64_t m, xsz;
+
+                assert(a->after->new_size != UINT64_MAX);
+                m = a->after->new_size + span;
+
+                xsz = partition_max_size(a->after);
+                if (xsz != UINT64_MAX && m > xsz)
+                        m = xsz;
+
+                span = charge_size(span, m - a->after->new_size);
+                a->after->new_size = m;
+        }
+
+        /* What? Even still some space left (maybe because there was no preceeding partition, or it had a
+         * size limit), then let's donate it to whoever wants it. */
+        if (span > 0) {
+                Partition *p;
+
+                LIST_FOREACH(partitions, p, context->partitions) {
+                        uint64_t m, xsz;
+
+                        if (p->allocated_to_area != a)
+                                continue;
+
+                        if (PARTITION_IS_FOREIGN(p))
+                                continue;
+
+                        assert(p->new_size != UINT64_MAX);
+                        m = p->new_size + span;
+
+                        xsz = partition_max_size(a->after);
+                        if (xsz != UINT64_MAX && m > xsz)
+                                m = xsz;
+
+                        span = charge_size(span, m - p->new_size);
+                        p->new_size = m;
+
+                        if (span == 0)
+                                break;
+                }
+        }
+
+        /* Yuck, still noone? Then make it padding */
+        if (span > 0 && a->after) {
+                assert(a->after->new_padding != UINT64_MAX);
+                a->after->new_padding += span;
+        }
+
+        return 0;
+}
+
+static int context_grow_partitions(Context *context) {
+        Partition *p;
+        int r;
+
+        assert(context);
+
+        for (size_t i = 0; i < context->n_free_areas; i++) {
+                r = context_grow_partitions_on_free_area(context, context->free_areas[i]);
+                if (r < 0)
+                        return r;
+        }
+
+        /* All existing partitions that have no free space after them can't change size */
+        LIST_FOREACH(partitions, p, context->partitions) {
+                if (p->dropped)
+                        continue;
+
+                if (!PARTITION_EXISTS(p) || p->padding_area) {
+                        /* The algorithm above must have initialized this already */
+                        assert(p->new_size != UINT64_MAX);
+                        continue;
+                }
+
+                assert(p->new_size == UINT64_MAX);
+                p->new_size = p->current_size;
+
+                assert(p->new_padding == UINT64_MAX);
+                p->new_padding = p->current_padding;
+        }
+
+        return 0;
+}
+
+static void context_place_partitions(Context *context) {
+        uint64_t partno = 0;
+        Partition *p;
+
+        assert(context);
+
+        /* Determine next partition number to assign */
+        LIST_FOREACH(partitions, p, context->partitions) {
+                if (!PARTITION_EXISTS(p))
+                        continue;
+
+                assert(p->partno != UINT64_MAX);
+                if (p->partno >= partno)
+                        partno = p->partno + 1;
+        }
+
+        for (size_t i = 0; i < context->n_free_areas; i++) {
+                FreeArea *a = context->free_areas[i];
+                uint64_t start, left;
+
+                if (a->after) {
+                        assert(a->after->offset != UINT64_MAX);
+                        assert(a->after->new_size != UINT64_MAX);
+                        assert(a->after->new_padding != UINT64_MAX);
+
+                        start = a->after->offset + a->after->new_size + a->after->new_padding;
+                } else
+                        start = context->start;
+
+                start = round_up_size(start, 4096);
+                left = a->size;
+
+                LIST_FOREACH(partitions, p, context->partitions) {
+                        if (p->allocated_to_area != a)
+                                continue;
+
+                        p->offset = start;
+                        p->partno = partno++;
+
+                        assert(left >= p->new_size);
+                        start += p->new_size;
+                        left -= p->new_size;
+
+                        assert(left >= p->new_padding);
+                        start += p->new_padding;
+                        left -= p->new_padding;
+                }
+        }
+}
+
+static int config_parse_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) {
+
+        sd_id128_t *type_uuid = data;
+        int r;
+
+        assert(rvalue);
+        assert(type_uuid);
+
+        r = gpt_partition_type_uuid_from_string(rvalue, type_uuid);
+        if (r < 0)
+                return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse partition type: %s", rvalue);
+
+        return 0;
+}
+
+static int config_parse_label(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        _cleanup_free_ char16_t *recoded = NULL;
+        char **label = data;
+        int r;
+
+        assert(rvalue);
+        assert(label);
+
+        if (!utf8_is_valid(rvalue)) {
+                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));
+        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);
+                return 0;
+        }
+
+        r = free_and_strdup(label, rvalue);
+        if (r < 0)
+                return log_oom();
+
+        return 0;
+}
+
+static int config_parse_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) {
+
+        uint32_t *priority = data, v;
+        int r;
+
+        assert(rvalue);
+        assert(priority);
+
+        r = safe_atou32(rvalue, &v);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse weight value, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        if (v > 1000U*1000U) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Weight needs to be in range 0…10000000, ignoring: %" PRIu32, v);
+                return 0;
+        }
+
+        *priority = v;
+        return 0;
+}
+
+static int config_parse_size4096(
+                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) {
+
+        uint64_t *sz = data, parsed;
+        int r;
+
+        assert(rvalue);
+        assert(data);
+
+        r = parse_size(rvalue, 1024, &parsed);
+        if (r < 0)
+                return log_syntax(unit, LOG_WARNING, filename, line, r,
+                                  "Failed to parse size value: %s", rvalue);
+
+        if (ltype > 0)
+                *sz = round_up_size(parsed, 4096);
+        else if (ltype < 0)
+                *sz = round_down_size(parsed, 4096);
+        else
+                *sz = parsed;
+
+        if (*sz != parsed)
+                log_syntax(unit, LOG_NOTICE, filename, line, r, "Rounded %s= size %" PRIu64 " → %" PRIu64 ", a multiple of 4096.", lvalue, parsed, *sz);
+
+        return 0;
+}
+
+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  },
+                {}
+        };
+        int r;
+
+        r = config_parse(NULL, path, NULL, "Partition\0", config_item_table_lookup, table, CONFIG_PARSE_WARN, p);
+        if (r < 0)
+                return r;
+
+        if (p->size_min != UINT64_MAX && p->size_max != UINT64_MAX && p->size_min > p->size_max)
+                return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+                                  "SizeMinBytes= larger than SizeMaxBytes=, refusing.");
+
+        if (p->padding_min != UINT64_MAX && p->padding_max != UINT64_MAX && p->padding_min > p->padding_max)
+                return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+                                  "PaddingMinBytes= larger than PaddingMaxBytes=, refusing.");
+
+        if (sd_id128_is_null(p->type_uuid))
+                return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
+                                  "Type= not defined, refusing.");
+
+        return 0;
+}
+
+static int context_read_definitions(
+                Context *context,
+                const char *directory,
+                const char *root) {
+
+        _cleanup_strv_free_ char **files = NULL;
+        Partition *last = NULL;
+        char **f;
+        int r;
+
+        assert(context);
+
+        if (directory)
+                r = conf_files_list_strv(&files, ".conf", NULL, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char**) STRV_MAKE(directory));
+        else
+                r = conf_files_list_strv(&files, ".conf", root, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char**) CONF_PATHS_STRV("repart.d"));
+        if (r < 0)
+                return log_error_errno(r, "Failed to enumerate *.conf files: %m");
+
+        STRV_FOREACH(f, files) {
+                _cleanup_(partition_freep) Partition *p = NULL;
+
+                p = partition_new();
+                if (!p)
+                        return log_oom();
+
+                p->definition_path = strdup(*f);
+                if (!p->definition_path)
+                        return log_oom();
+
+                r = partition_read_definition(p, *f);
+                if (r < 0)
+                        return r;
+
+                LIST_INSERT_AFTER(partitions, context->partitions, last, p);
+                last = TAKE_PTR(p);
+                context->n_partitions++;
+        }
+
+        return 0;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct fdisk_context*, fdisk_unref_context);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct fdisk_partition*, fdisk_unref_partition);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct fdisk_parttype*, fdisk_unref_parttype);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct fdisk_table*, fdisk_unref_table);
+
+static int determine_current_padding(
+                struct fdisk_context *c,
+                struct fdisk_table *t,
+                struct fdisk_partition *p,
+                uint64_t *ret) {
+
+        size_t n_partitions;
+        uint64_t offset, next = UINT64_MAX;
+
+        assert(c);
+        assert(t);
+        assert(p);
+
+        if (!fdisk_partition_has_end(p))
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition has no end!");
+
+        offset = fdisk_partition_get_end(p);
+        assert(offset < UINT64_MAX / 512);
+        offset *= 512;
+
+        n_partitions = fdisk_table_get_nents(t);
+        for (size_t i = 0; i < n_partitions; i++)  {
+                struct fdisk_partition *q;
+                uint64_t start;
+
+                q = fdisk_table_get_partition(t, i);
+                if (!q)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read partition metadata: %m");
+
+                if (fdisk_partition_is_used(q) <= 0)
+                        continue;
+
+                if (!fdisk_partition_has_start(q))
+                        continue;
+
+                start = fdisk_partition_get_start(q);
+                assert(start < UINT64_MAX / 512);
+                start *= 512;
+
+                if (start >= offset && (next == UINT64_MAX || next > start))
+                        next = start;
+        }
+
+        if (next == UINT64_MAX) {
+                /* No later partition? In that case check the end of the usable area */
+                next = fdisk_get_last_lba(c);
+                assert(next < UINT64_MAX);
+                next++; /* The last LBA is one sector before the end */
+
+                assert(next < UINT64_MAX / 512);
+                next *= 512;
+
+                if (offset > next)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition end beyond disk end.");
+        }
+
+        assert(next >= offset);
+        offset = round_up_size(offset, 4096);
+        next = round_down_size(next, 4096);
+
+        if (next >= offset) /* Check again, rounding might have fucked things up */
+                *ret = next - offset;
+        else
+                *ret = 0;
+
+        return 0;
+}
+
+static int fdisk_ask_cb(struct fdisk_context *c, struct fdisk_ask *ask, void *data) {
+        _cleanup_free_ char *ids = NULL;
+        int r;
+
+        if (fdisk_ask_get_type(ask) != FDISK_ASKTYPE_STRING)
+                return -EINVAL;
+
+        ids = new(char, ID128_UUID_STRING_MAX);
+        if (!ids)
+                return -ENOMEM;
+
+        r = fdisk_ask_string_set_result(ask, id128_to_uuid_string(*(sd_id128_t*) data, ids));
+        if (r < 0)
+                return r;
+
+        TAKE_PTR(ids);
+        return 0;
+}
+
+static int fdisk_set_disklabel_id_by_uuid(struct fdisk_context *c, sd_id128_t id) {
+        int r;
+
+        r = fdisk_set_ask(c, fdisk_ask_cb, &id);
+        if (r < 0)
+                return r;
+
+        r = fdisk_set_disklabel_id(c);
+        if (r < 0)
+                return r;
+
+        return fdisk_set_ask(c, NULL, NULL);
+}
+
+#define DISK_UUID_TOKEN "disk-uuid"
+
+static int disk_acquire_uuid(Context *context, sd_id128_t *ret) {
+        union {
+                unsigned char md[SHA256_DIGEST_LENGTH];
+                sd_id128_t id;
+        } result;
+
+        assert(context);
+        assert(ret);
+
+        /* Calculate the HMAC-SHA256 of the string "disk-uuid", keyed off the machine ID. We use the machine
+         * ID as key (and not as cleartext!) since it's the machine ID we don't want to leak. */
+
+        if (!HMAC(EVP_sha256(),
+                  &context->seed, sizeof(context->seed),
+                  (const unsigned char*) DISK_UUID_TOKEN, strlen(DISK_UUID_TOKEN),
+                  result.md, NULL))
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "HMAC-SHA256 calculation failed.");
+
+        /* Take the first half, mark it as v4 UUID */
+        assert_cc(sizeof(result.md) == sizeof(result.id) * 2);
+        *ret = id128_make_v4_uuid(result.id);
+        return 0;
+}
+
+static int context_load_partition_table(Context *context, const char *node) {
+        _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;
+        _cleanup_free_ char *disk_uuid_string = NULL;
+        bool from_scratch = false;
+        sd_id128_t disk_uuid;
+        size_t n_partitions;
+        int r;
+
+        assert(context);
+        assert(node);
+
+        c = fdisk_new_context();
+        if (!c)
+                return log_oom();
+
+        r = fdisk_assign_device(c, node, arg_dry_run);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open device: %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)
+                return log_error_errno(errno, "Failed to lock block device: %m");
+
+        switch (arg_empty) {
+
+        case EMPTY_REFUSE:
+                /* Refuse empty disks, insist on an existing GPT partition table */
+                if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT))
+                        return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has no GPT disk label, not repartitioning.", node);
+
+                break;
+
+        case EMPTY_REQUIRE:
+                /* Require an empty disk, refuse any existing partition table */
+                r = fdisk_has_label(c);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to determine whether disk %s has a disk label: %m", node);
+                if (r > 0)
+                        return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s already has a disk label, refusing.", node);
+
+                from_scratch = true;
+                break;
+
+        case EMPTY_ALLOW:
+                /* Allow both an empty disk and an existing partition table, but only GPT */
+                r = fdisk_has_label(c);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to determine whether disk %s has a disk label: %m", node);
+                if (r > 0) {
+                        if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT))
+                                return log_notice_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has non-GPT disk label, not repartitioning.", node);
+                } else
+                        from_scratch = true;
+
+                break;
+
+        case EMPTY_FORCE:
+                /* Always reinitiaize the disk, don't consider what there was on the disk before */
+                from_scratch = true;
+                break;
+        }
+
+        if (from_scratch) {
+                r = fdisk_enable_wipe(c, true);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to enable wiping of disk signature: %m");
+
+                r = fdisk_create_disklabel(c, "gpt");
+                if (r < 0)
+                        return log_error_errno(r, "Failed to create GPT disk label: %m");
+
+                r = disk_acquire_uuid(context, &disk_uuid);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to acquire disk GPT uuid: %m");
+
+                r = fdisk_set_disklabel_id_by_uuid(c, disk_uuid);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set GPT disk label: %m");
+
+                goto add_initial_free_area;
+        }
+
+        r = fdisk_get_disklabel_id(c, &disk_uuid_string);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get current GPT disk label UUID: %m");
+
+        r = sd_id128_from_string(disk_uuid_string, &disk_uuid);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse current GPT disk label UUID: %m");
+
+        if (sd_id128_is_null(disk_uuid)) {
+                r = disk_acquire_uuid(context, &disk_uuid);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to acquire disk GPT uuid: %m");
+
+                r = fdisk_set_disklabel_id(c);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set GPT disk label: %m");
+        }
+
+        r = fdisk_get_partitions(c, &t);
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire partition table: %m");
+
+        n_partitions = fdisk_table_get_nents(t);
+        for (size_t i = 0; i < n_partitions; i++)  {
+                _cleanup_free_ char *label_copy = NULL;
+                Partition *pp, *last = NULL;
+                struct fdisk_partition *p;
+                struct fdisk_parttype *pt;
+                const char *pts, *ids, *label;
+                uint64_t sz, start;
+                bool found = false;
+                sd_id128_t ptid, id;
+                size_t partno;
+
+                p = fdisk_table_get_partition(t, i);
+                if (!p)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read partition metadata: %m");
+
+                if (fdisk_partition_is_used(p) <= 0)
+                        continue;
+
+                if (fdisk_partition_has_start(p) <= 0 ||
+                    fdisk_partition_has_size(p) <= 0 ||
+                    fdisk_partition_has_partno(p) <= 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found a partition without a position, size or number.");
+
+                pt = fdisk_partition_get_type(p);
+                if (!pt)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire type of partition: %m");
+
+                pts = fdisk_parttype_get_string(pt);
+                if (!pts)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire type of partition as string: %m");
+
+                r = sd_id128_from_string(pts, &ptid);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse partition type UUID %s: %m", pts);
+
+                ids = fdisk_partition_get_uuid(p);
+                if (!ids)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found a partition without a UUID.");
+
+                r = sd_id128_from_string(ids, &id);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse partition UUID %s: %m", ids);
+
+                label = fdisk_partition_get_name(p);
+                if (!isempty(label)) {
+                        label_copy = strdup(label);
+                        if (!label_copy)
+                                return log_oom();
+                }
+
+                sz = fdisk_partition_get_size(p);
+                assert_se(sz <= UINT64_MAX/512);
+                sz *= 512;
+
+                start = fdisk_partition_get_start(p);
+                assert_se(start <= UINT64_MAX/512);
+                start *= 512;
+
+                partno = fdisk_partition_get_partno(p);
+
+                if (left_boundary == UINT64_MAX || left_boundary > start)
+                        left_boundary = start;
+
+                /* Assign this existing partition to the first partition of the right type that doesn't have
+                 * an existing one assigned yet. */
+                LIST_FOREACH(partitions, pp, context->partitions) {
+                        last = pp;
+
+                        if (!sd_id128_equal(pp->type_uuid, ptid))
+                                continue;
+
+                        if (!pp->current_partition) {
+                                pp->current_uuid = id;
+                                pp->current_size = sz;
+                                pp->offset = start;
+                                pp->partno = partno;
+                                pp->current_label = TAKE_PTR(label_copy);
+
+                                pp->current_partition = p;
+                                fdisk_ref_partition(p);
+
+                                r = determine_current_padding(c, t, p, &pp->current_padding);
+                                if (r < 0)
+                                        return r;
+
+                                if (pp->current_padding > 0) {
+                                        r = context_add_free_area(context, pp->current_padding, pp);
+                                        if (r < 0)
+                                                return r;
+                                }
+
+                                found = true;
+                                break;
+                        }
+                }
+
+                /* If we have no matching definition, create a new one. */
+                if (!found) {
+                        _cleanup_(partition_freep) Partition *np = NULL;
+
+                        np = partition_new();
+                        if (!np)
+                                return log_oom();
+
+                        np->current_uuid = id;
+                        np->type_uuid = ptid;
+                        np->current_size = sz;
+                        np->offset = start;
+                        np->partno = partno;
+                        np->current_label = TAKE_PTR(label_copy);
+
+                        np->current_partition = p;
+                        fdisk_ref_partition(p);
+
+                        r = determine_current_padding(c, t, p, &np->current_padding);
+                        if (r < 0)
+                                return r;
+
+                        if (np->current_padding > 0) {
+                                r = context_add_free_area(context, np->current_padding, np);
+                                if (r < 0)
+                                        return r;
+                        }
+
+                        LIST_INSERT_AFTER(partitions, context->partitions, last, TAKE_PTR(np));
+                        context->n_partitions++;
+                }
+        }
+
+add_initial_free_area:
+        nsectors = fdisk_get_nsectors(c);
+        assert(nsectors <= UINT64_MAX/512);
+        nsectors *= 512;
+
+        first_lba = fdisk_get_first_lba(c);
+        assert(first_lba <= UINT64_MAX/512);
+        first_lba *= 512;
+
+        last_lba = fdisk_get_last_lba(c);
+        assert(last_lba < UINT64_MAX);
+        last_lba++;
+        assert(last_lba <= UINT64_MAX/512);
+        last_lba *= 512;
+
+        assert(last_lba >= first_lba);
+
+        if (left_boundary == UINT64_MAX) {
+                /* No partitions at all? Then the whole disk is up for grabs. */
+
+                first_lba = round_up_size(first_lba, 4096);
+                last_lba = round_down_size(last_lba, 4096);
+
+                if (last_lba > first_lba) {
+                        r = context_add_free_area(context, last_lba - first_lba, NULL);
+                        if (r < 0)
+                                return r;
+                }
+        } else {
+                /* Add space left of first partition */
+                assert(left_boundary >= first_lba);
+
+                first_lba = round_up_size(first_lba, 4096);
+                left_boundary = round_down_size(left_boundary, 4096);
+                last_lba = round_down_size(last_lba, 4096);
+
+                if (left_boundary > first_lba) {
+                        r = context_add_free_area(context, left_boundary - first_lba, NULL);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        context->start = first_lba;
+        context->end = last_lba;
+        context->total = nsectors;
+        context->fdisk_context = TAKE_PTR(c);
+
+        return from_scratch;
+}
+
+static void context_unload_partition_table(Context *context) {
+        Partition *p, *next;
+
+        assert(context);
+
+        LIST_FOREACH_SAFE(partitions, p, next, context->partitions) {
+
+                /* Entirely remove partitions that have no configuration */
+                if (PARTITION_IS_FOREIGN(p)) {
+                        partition_unlink_and_free(context, p);
+                        continue;
+                }
+
+                /* Otherwise drop all data we read off the block device and everything we might have
+                 * calculated based on it */
+
+                p->dropped = false;
+                p->current_size = UINT64_MAX;
+                p->new_size = UINT64_MAX;
+                p->current_padding = UINT64_MAX;
+                p->new_padding = UINT64_MAX;
+                p->partno = UINT64_MAX;
+                p->offset = UINT64_MAX;
+
+                if (p->current_partition) {
+                        fdisk_unref_partition(p->current_partition);
+                        p->current_partition = NULL;
+                }
+
+                if (p->new_partition) {
+                        fdisk_unref_partition(p->new_partition);
+                        p->new_partition = NULL;
+                }
+
+                p->padding_area = NULL;
+                p->allocated_to_area = NULL;
+
+                p->current_uuid = p->new_uuid = SD_ID128_NULL;
+        }
+
+        context->start = UINT64_MAX;
+        context->end = UINT64_MAX;
+        context->total = UINT64_MAX;
+
+        if (context->fdisk_context) {
+                fdisk_unref_context(context->fdisk_context);
+                context->fdisk_context = NULL;
+        }
+
+        context_free_free_areas(context);
+}
+
+static int format_size_change(uint64_t from, uint64_t to, char **ret) {
+        char format_buffer1[FORMAT_BYTES_MAX], format_buffer2[FORMAT_BYTES_MAX], *buf;
+
+        if (from != UINT64_MAX)
+                format_bytes(format_buffer1, sizeof(format_buffer1), from);
+        if (to != UINT64_MAX)
+                format_bytes(format_buffer2, sizeof(format_buffer2), to);
+
+        if (from != UINT64_MAX) {
+                if (from == to || to == UINT64_MAX)
+                        buf = strdup(format_buffer1);
+                else
+                        buf = strjoin(format_buffer1, " ", special_glyph(SPECIAL_GLYPH_ARROW), " ", format_buffer2);
+        } else if (to != UINT64_MAX)
+                buf = strjoin(special_glyph(SPECIAL_GLYPH_ARROW), " ", format_buffer2);
+        else {
+                *ret = NULL;
+                return 0;
+        }
+
+        if (!buf)
+                return log_oom();
+
+        *ret = TAKE_PTR(buf);
+        return 1;
+}
+
+static const char *partition_label(const Partition *p) {
+        assert(p);
+
+        if (p->new_label)
+                return p->new_label;
+
+        if (p->current_label)
+                return p->current_label;
+
+        return gpt_partition_type_uuid_to_string(p->type_uuid);
+}
+
+static int context_dump_partitions(Context *context, const char *node) {
+        _cleanup_(table_unrefp) Table *t = NULL;
+        uint64_t sum_padding = 0, sum_size = 0;
+        Partition *p;
+        int r;
+
+        t = table_new("type", "label", "uuid", "file", "node", "offset", "raw size", "size", "raw padding", "padding");
+        if (!t)
+                return log_oom();
+
+        if (!DEBUG_LOGGING)
+                (void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4, (size_t) 7, (size_t) 9, (size_t) -1);
+
+        (void) table_set_align_percent(t, table_get_cell(t, 0, 4), 100);
+        (void) table_set_align_percent(t, table_get_cell(t, 0, 5), 100);
+
+        LIST_FOREACH(partitions, p, context->partitions) {
+                _cleanup_free_ char *size_change = NULL, *padding_change = NULL, *partname = NULL;
+                char uuid_buffer[ID128_UUID_STRING_MAX];
+                const char *label;
+
+                if (p->dropped)
+                        continue;
+
+                label = partition_label(p);
+                partname = p->partno != UINT64_MAX ? fdisk_partname(node, p->partno+1) : NULL;
+
+                r = format_size_change(p->current_size, p->new_size, &size_change);
+                if (r < 0)
+                        return r;
+
+                r = format_size_change(p->current_padding, p->new_padding, &padding_change);
+                if (r < 0)
+                        return r;
+
+                if (p->new_size != UINT64_MAX)
+                        sum_size += p->new_size;
+                if (p->new_padding != UINT64_MAX)
+                        sum_padding += p->new_padding;
+
+                r = table_add_many(
+                                t,
+                                TABLE_STRING, gpt_partition_type_uuid_to_string_harder(p->type_uuid, uuid_buffer),
+                                TABLE_STRING, label ?: "-", TABLE_SET_COLOR, label ? NULL : ansi_grey(),
+                                TABLE_UUID, sd_id128_is_null(p->new_uuid) ? p->current_uuid : p->new_uuid,
+                                TABLE_STRING, p->definition_path ? basename(p->definition_path) : "-", TABLE_SET_COLOR, p->definition_path ? NULL : ansi_grey(),
+                                TABLE_STRING, partname ?: "no", TABLE_SET_COLOR, partname ? NULL : ansi_highlight(),
+                                TABLE_UINT64, p->offset,
+                                TABLE_UINT64, p->new_size,
+                                TABLE_STRING, size_change, TABLE_SET_COLOR, !p->partitions_next && sum_size > 0 ? ansi_underline() : NULL,
+                                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");
+        }
+
+        if (sum_padding > 0 || sum_size > 0) {
+                char s[FORMAT_BYTES_MAX];
+                const char *a, *b;
+
+                a = strjoina(special_glyph(SPECIAL_GLYPH_SIGMA), " = ", format_bytes(s, sizeof(s), sum_size));
+                b = strjoina(special_glyph(SPECIAL_GLYPH_SIGMA), " = ", format_bytes(s, sizeof(s), sum_padding));
+
+                r = table_add_many(
+                                t,
+                                TABLE_EMPTY,
+                                TABLE_EMPTY,
+                                TABLE_EMPTY,
+                                TABLE_EMPTY,
+                                TABLE_EMPTY,
+                                TABLE_EMPTY,
+                                TABLE_EMPTY,
+                                TABLE_STRING, a,
+                                TABLE_EMPTY,
+                                TABLE_STRING, b);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add row to table: %m");
+        }
+
+        r = table_print(t, stdout);
+        if (r < 0)
+                return log_error_errno(r, "Failed to dump table: %m");
+
+        return 0;
+}
+
+static void context_bar_char_process_partition(
+                Context *context,
+                Partition *bar[],
+                size_t n,
+                Partition *p,
+                size_t *ret_start) {
+
+        uint64_t from, to, total;
+        size_t x, y;
+
+        assert(context);
+        assert(bar);
+        assert(n > 0);
+        assert(p);
+
+        if (p->dropped)
+                return;
+
+        assert(p->offset != UINT64_MAX);
+        assert(p->new_size != UINT64_MAX);
+
+        from = p->offset;
+        to = from + p->new_size;
+
+        assert(context->end >= context->start);
+        total = context->end - context->start;
+
+        assert(from >= context->start);
+        assert(from <= context->end);
+        x = (from - context->start) * n / total;
+
+        assert(to >= context->start);
+        assert(to <= context->end);
+        y = (to - context->start) * n / total;
+
+        assert(x <= y);
+        assert(y <= n);
+
+        for (size_t i = x; i < y; i++)
+                bar[i] = p;
+
+        *ret_start = x;
+}
+
+static int partition_hint(const Partition *p, const char *node, char **ret) {
+        _cleanup_free_ char *buf = NULL;
+        char ids[ID128_UUID_STRING_MAX];
+        const char *label;
+        sd_id128_t id;
+
+        /* Tries really hard to find a suitable description for this partition */
+
+        if (p->definition_path) {
+                buf = strdup(basename(p->definition_path));
+                goto done;
+        }
+
+        label = partition_label(p);
+        if (!isempty(label)) {
+                buf = strdup(label);
+                goto done;
+        }
+
+        if (p->partno != UINT64_MAX) {
+                buf = fdisk_partname(node, p->partno+1);
+                goto done;
+        }
+
+        if (!sd_id128_is_null(p->new_uuid))
+                id = p->new_uuid;
+        else if (!sd_id128_is_null(p->current_uuid))
+                id = p->current_uuid;
+        else
+                id = p->type_uuid;
+
+        buf = strdup(id128_to_uuid_string(id, ids));
+
+done:
+        if (!buf)
+                return -ENOMEM;
+
+        *ret = TAKE_PTR(buf);
+        return 0;
+}
+
+static int context_dump_partition_bar(Context *context, const char *node) {
+        _cleanup_free_ Partition **bar = NULL;
+        _cleanup_free_ size_t *start_array = NULL;
+        Partition *p, *last = NULL;
+        bool z = false;
+        size_t c, j = 0;
+
+        assert((c = columns()) >= 2);
+        c -= 2; /* We do not use the leftmost and rightmost character cell */
+
+        bar = new0(Partition*, c);
+        if (!bar)
+                return log_oom();
+
+        start_array = new(size_t, context->n_partitions);
+        if (!start_array)
+                return log_oom();
+
+        LIST_FOREACH(partitions, p, context->partitions)
+                context_bar_char_process_partition(context, bar, c, p, start_array + j++);
+
+        putc(' ', stdout);
+
+        for (size_t i = 0; i < c; i++) {
+                if (bar[i]) {
+                        if (last != bar[i])
+                                z = !z;
+
+                        fputs(z ? ansi_green() : ansi_yellow(), stdout);
+                        fputs(special_glyph(SPECIAL_GLYPH_DARK_SHADE), stdout);
+                } else {
+                        fputs(ansi_normal(), stdout);
+                        fputs(special_glyph(SPECIAL_GLYPH_LIGHT_SHADE), stdout);
+                }
+
+                last = bar[i];
+        }
+
+        fputs(ansi_normal(), stdout);
+        putc('\n', stdout);
+
+        for (size_t i = 0; i < context->n_partitions; i++) {
+                _cleanup_free_ char **line = NULL;
+
+                line = new0(char*, c);
+                if (!line)
+                        return log_oom();
+
+                j = 0;
+                LIST_FOREACH(partitions, p, context->partitions) {
+                        _cleanup_free_ char *d = NULL;
+                        j++;
+
+                        if (i < context->n_partitions - j) {
+
+                                if (line[start_array[j-1]]) {
+                                        const char *e;
+
+                                        /* Upgrade final corner to the right with a branch to the right */
+                                        e = startswith(line[start_array[j-1]], special_glyph(SPECIAL_GLYPH_TREE_RIGHT));
+                                        if (e) {
+                                                d = strjoin(special_glyph(SPECIAL_GLYPH_TREE_BRANCH), e);
+                                                if (!d)
+                                                        return log_oom();
+                                        }
+                                }
+
+                                if (!d) {
+                                        d = strdup(special_glyph(SPECIAL_GLYPH_TREE_VERTICAL));
+                                        if (!d)
+                                                return log_oom();
+                                }
+
+                        } else if (i == context->n_partitions - j) {
+                                _cleanup_free_ char *hint = NULL;
+
+                                (void) partition_hint(p, node, &hint);
+
+                                if (streq_ptr(line[start_array[j-1]], special_glyph(SPECIAL_GLYPH_TREE_VERTICAL)))
+                                        d = strjoin(special_glyph(SPECIAL_GLYPH_TREE_BRANCH), " ", strna(hint));
+                                else
+                                        d = strjoin(special_glyph(SPECIAL_GLYPH_TREE_RIGHT), " ", strna(hint));
+
+                                if (!d)
+                                        return log_oom();
+                        }
+
+                        if (d)
+                                free_and_replace(line[start_array[j-1]], d);
+                }
+
+                putc(' ', stdout);
+
+                j = 0;
+                while (j < c) {
+                        if (line[j]) {
+                                fputs(line[j], stdout);
+                                j += utf8_console_width(line[j]);
+                        } else {
+                                putc(' ', stdout);
+                                j++;
+                        }
+                }
+
+                putc('\n', stdout);
+
+                for (j = 0; j < c; j++)
+                        free(line[j]);
+        }
+
+        return 0;
+}
+
+static bool context_changed(const Context *context) {
+        Partition *p;
+
+        LIST_FOREACH(partitions, p, context->partitions) {
+                if (p->dropped)
+                        continue;
+
+                if (p->allocated_to_area)
+                        return true;
+
+                if (p->new_size != p->current_size)
+                        return true;
+        }
+
+        return false;
+}
+
+static int context_wipe_partition(Context *context, Partition *p) {
+        _cleanup_(blkid_free_probep) blkid_probe probe = NULL;
+        int r;
+
+        assert(context);
+        assert(p);
+        assert(!PARTITION_EXISTS(p)); /* Safety check: never wipe existing partitions */
+
+        probe = blkid_new_probe();
+        if (!probe)
+                return log_oom();
+
+        assert(p->offset != UINT64_MAX);
+        assert(p->new_size != UINT64_MAX);
+
+        errno = 0;
+        r = blkid_probe_set_device(probe, fdisk_get_devfd(context->fdisk_context), p->offset, p->new_size);
+        if (r < 0)
+                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to allocate device probe for partition %" PRIu64 ".", p->partno);
+
+        errno = 0;
+        if (blkid_probe_enable_superblocks(probe, true) < 0 ||
+            blkid_probe_set_superblocks_flags(probe, BLKID_SUBLKS_MAGIC|BLKID_SUBLKS_BADCSUM) < 0 ||
+            blkid_probe_enable_partitions(probe, true) < 0 ||
+            blkid_probe_set_partitions_flags(probe, BLKID_PARTS_MAGIC) < 0)
+                return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to enable superblock and partition probing for partition %" PRIu64 ".", p->partno);
+
+        for (;;) {
+                errno = 0;
+                r = blkid_do_probe(probe);
+                if (r < 0)
+                        return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe for file systems.");
+                if (r > 0)
+                        break;
+
+                errno = 0;
+                if (blkid_do_wipe(probe, false) < 0)
+                        return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to wipe file system signature.");
+        }
+
+        log_info("Successfully wiped file system signatures from partition %" PRIu64 ".", p->partno);
+        return 0;
+}
+
+static int context_discard_range(Context *context, uint64_t offset, uint64_t size) {
+        struct stat st;
+        int fd;
+
+        assert(context);
+        assert(offset != UINT64_MAX);
+        assert(size != UINT64_MAX);
+
+        if (size <= 0)
+                return 0;
+
+        fd = fdisk_get_devfd(context->fdisk_context);
+        assert(fd >= 0);
+
+        if (fstat(fd, &st) < 0)
+                return -errno;
+
+        if (S_ISREG(st.st_mode)) {
+                if (fallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, offset, size) < 0) {
+                        if (ERRNO_IS_NOT_SUPPORTED(errno))
+                                return -EOPNOTSUPP;
+
+                        return -errno;
+                }
+
+                return 1;
+        }
+
+        if (S_ISBLK(st.st_mode)) {
+                uint64_t range[2], end;
+
+                range[0] = round_up_size(offset, 512);
+
+                end = offset + size;
+                if (end <= range[0])
+                        return 0;
+
+                range[1] = round_down_size(end - range[0], 512);
+                if (range[1] <= 0)
+                        return 0;
+
+                if (ioctl(fd, BLKDISCARD, range) < 0) {
+                        if (ERRNO_IS_NOT_SUPPORTED(errno))
+                                return -EOPNOTSUPP;
+
+                        return -errno;
+                }
+
+                return 1;
+        }
+
+        return -EOPNOTSUPP;
+}
+
+static int context_discard_partition(Context *context, Partition *p) {
+        int r;
+
+        assert(context);
+        assert(p);
+
+        assert(p->offset != UINT64_MAX);
+        assert(p->new_size != UINT64_MAX);
+        assert(!PARTITION_EXISTS(p)); /* Safety check: never discard existing partitions */
+
+        if (!arg_discard)
+                return 0;
+
+        r = context_discard_range(context, p->offset, p->new_size);
+        if (r == -EOPNOTSUPP) {
+                log_info("Storage does not support discarding, not discarding data in new partition %" PRIu64 ".", p->partno);
+                return 0;
+        }
+        if (r == 0) {
+                log_info("Partition %" PRIu64 " too short for discard, skipping.", p->partno);
+                return 0;
+        }
+        if (r < 0)
+                return log_error_errno(r, "Failed to discard data for new partition %" PRIu64 ".", p->partno);
+
+        log_info("Successfully discarded data from partition %" PRIu64 ".", p->partno);
+        return 1;
+}
+
+static int context_discard_gap_after(Context *context, Partition *p) {
+        uint64_t gap, next = UINT64_MAX;
+        Partition *q;
+        int r;
+
+        assert(context);
+        assert(!p || (p->offset != UINT64_MAX && p->new_size != UINT64_MAX));
+
+        if (p)
+                gap = p->offset + p->new_size;
+        else
+                gap = context->start;
+
+        LIST_FOREACH(partitions, q, context->partitions) {
+                if (q->dropped)
+                        continue;
+
+                assert(q->offset != UINT64_MAX);
+                assert(q->new_size != UINT64_MAX);
+
+                if (q->offset < gap)
+                        continue;
+
+                if (next == UINT64_MAX || q->offset < next)
+                        next = q->offset;
+        }
+
+        if (next == UINT64_MAX) {
+                next = context->end;
+                if (gap > next)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition end beyond disk end.");
+        }
+
+        assert(next >= gap);
+        r = context_discard_range(context, gap, next - gap);
+        if (r == -EOPNOTSUPP) {
+                if (p)
+                        log_info("Storage does not support discarding, not discarding gap after partition %" PRIu64 ".", p->partno);
+                else
+                        log_info("Storage does not support discarding, not discarding gap at beginning of disk.");
+                return 0;
+        }
+        if (r == 0)  /* Too short */
+                return 0;
+        if (r < 0) {
+                if (p)
+                        return log_error_errno(r, "Failed to discard gap after partition %" PRIu64 ".", p->partno);
+                else
+                        return log_error_errno(r, "Failed to discard gap at beginning of disk.");
+        }
+
+        if (p)
+                log_info("Successfully discarded gap after partition %" PRIu64 ".", p->partno);
+        else
+                log_info("Successfully discarded gap at beginning of disk.");
+
+        return 0;
+}
+
+static int context_wipe_and_discard(Context *context, bool from_scratch) {
+        Partition *p;
+        int r;
+
+        assert(context);
+
+        /* Wipe and discard the contents of all partitions we are about to create. We skip the discarding if
+         * we were supposed to start from scratch anyway, as in that case we just discard the whole block
+         * device in one go early on. */
+
+        LIST_FOREACH(partitions, p, context->partitions) {
+
+                if (!p->allocated_to_area)
+                        continue;
+
+                if (!from_scratch) {
+                        r = context_discard_partition(context, p);
+                        if (r < 0)
+                                return r;
+                }
+
+                r = context_wipe_partition(context, p);
+                if (r < 0)
+                        return r;
+
+                if (!from_scratch) {
+                        r = context_discard_gap_after(context, p);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        if (!from_scratch) {
+                r = context_discard_gap_after(context, NULL);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *ret) {
+        struct {
+                sd_id128_t type_uuid;
+                uint64_t counter;
+        } _packed_  plaintext = {};
+        union {
+                unsigned char md[SHA256_DIGEST_LENGTH];
+                sd_id128_t id;
+        } result;
+
+        uint64_t k = 0;
+        Partition *q;
+        int r;
+
+        assert(context);
+        assert(p);
+        assert(ret);
+
+        /* Calculate a good UUID for the indicated partition. We want a certain degree of reproducibility,
+         * hence we won't generate the UUIDs randomly. Instead we use a cryptographic hash (precisely:
+         * HMAC-SHA256) to derive them from a single seed. The seed is generally the machine ID of the
+         * installation we are processing, but if random behaviour is desired can be random, too. We use the
+         * seed value as key for the HMAC (since the machine ID is something we generally don't want to leak)
+         * and the partition type as plaintext. The partition type is suffixed with a counter (only for the
+         * second and later partition of the same type) if we have more than one partition of the same
+         * time. Or in other words:
+         *
+         * With:
+         *     SEED := /etc/machine-id
+         *
+         * If first partition instance of type TYPE_UUID:
+         *     PARTITION_UUID := HMAC-SHA256(SEED, TYPE_UUID)
+         *
+         * For all later partition instances of type TYPE_UUID with INSTANCE being the LE64 encoded instance number:
+         *     PARTITION_UUID := HMAC-SHA256(SEED, TYPE_UUID || INSTANCE)
+         */
+
+        LIST_FOREACH(partitions, q, context->partitions) {
+                if (p == q)
+                        break;
+
+                if (!sd_id128_equal(p->type_uuid, q->type_uuid))
+                        continue;
+
+                k++;
+        }
+
+        plaintext.type_uuid = p->type_uuid;
+        plaintext.counter = htole64(k);
+
+        if (!HMAC(EVP_sha256(),
+                  &context->seed, sizeof(context->seed),
+                  (const unsigned char*) &plaintext, k == 0 ? sizeof(sd_id128_t) : sizeof(plaintext),
+                  result.md, NULL))
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "SHA256 calculation failed.");
+
+        /* Take the first half, mark it as v4 UUID */
+        assert_cc(sizeof(result.md) == sizeof(result.id) * 2);
+        result.id = id128_make_v4_uuid(result.id);
+
+        /* Ensure this partition UUID is actually unique, and there's no remaining partition from an earlier run? */
+        LIST_FOREACH(partitions, q, context->partitions) {
+                if (p == q)
+                        continue;
+
+                if (sd_id128_equal(q->current_uuid, result.id) ||
+                    sd_id128_equal(q->new_uuid, result.id)) {
+                        log_warning("Partition UUID calculated from seed for partition %" PRIu64 " exists already, reverting to randomized UUID.", p->partno);
+
+                        r = sd_id128_randomize(&result.id);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to generate randomized UUID: %m");
+
+                        break;
+                }
+        }
+
+        *ret = result.id;
+        return 0;
+}
+
+static int partition_acquire_label(Context *context, Partition *p, char **ret) {
+        _cleanup_free_ char *label = NULL;
+        const char *prefix;
+        unsigned k = 1;
+
+        assert(context);
+        assert(p);
+        assert(ret);
+
+        prefix = gpt_partition_type_uuid_to_string(p->type_uuid);
+        if (!prefix)
+                prefix = "linux";
+
+        for (;;) {
+                const char *ll = label ?: prefix;
+                bool retry = false;
+                Partition *q;
+
+                LIST_FOREACH(partitions, q, context->partitions) {
+                        if (p == q)
+                                break;
+
+                        if (streq_ptr(ll, q->current_label) ||
+                            streq_ptr(ll, q->new_label)) {
+                                retry = true;
+                                break;
+                        }
+                }
+
+                if (!retry)
+                        break;
+
+                label = mfree(label);
+
+
+                if (asprintf(&label, "%s-%u", prefix, ++k) < 0)
+                        return log_oom();
+        }
+
+        if (!label) {
+                label = strdup(prefix);
+                if (!label)
+                        return log_oom();
+        }
+
+        *ret = TAKE_PTR(label);
+        return 0;
+}
+
+static int context_acquire_partition_uuids_and_labels(Context *context) {
+        Partition *p;
+        int r;
+
+        assert(context);
+
+        LIST_FOREACH(partitions, p, context->partitions) {
+                assert(sd_id128_is_null(p->new_uuid));
+                assert(!p->new_label);
+
+                /* Never touch foreign partitions */
+                if (PARTITION_IS_FOREIGN(p)) {
+                        p->new_uuid = p->current_uuid;
+
+                        if (p->current_label) {
+                                p->new_label = strdup(p->current_label);
+                                if (!p->new_label)
+                                        return log_oom();
+                        }
+
+                        continue;
+                }
+
+                if (!sd_id128_is_null(p->current_uuid))
+                        p->new_uuid = p->current_uuid; /* Never change initialized UUIDs */
+                else {
+                        r = partition_acquire_uuid(context, p, &p->new_uuid);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (!isempty(p->current_label)) {
+                        p->new_label = strdup(p->current_label); /* never change initialized labels */
+                        if (!p->new_label)
+                                return log_oom();
+                } else {
+                        r = partition_acquire_label(context, p, &p->new_label);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        return 0;
+}
+
+static int device_kernel_partitions_supported(int fd) {
+        struct loop_info64 info;
+        struct stat st;
+
+        assert(fd >= 0);
+
+        if (fstat(fd, &st) < 0)
+                return log_error_errno(fd, "Failed to fstat() image file: %m");
+        if (!S_ISBLK(st.st_mode))
+                return false;
+
+        if (ioctl(fd, LOOP_GET_STATUS64, &info) < 0) {
+
+                if (ERRNO_IS_NOT_SUPPORTED(errno) || errno == EINVAL)
+                        return true; /* not a loopback device, let's assume partition are supported */
+
+                return log_error_errno(fd, "Failed to issue LOOP_GET_STATUS64 on block device: %m");
+        }
+
+#if HAVE_VALGRIND_MEMCHECK_H
+        /* Valgrind currently doesn't know LOOP_GET_STATUS64. Remove this once it does */
+        VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
+#endif
+
+        return FLAGS_SET(info.lo_flags, LO_FLAGS_PARTSCAN);
+}
+
+static int context_write_partition_table(
+                Context *context,
+                const char *node,
+                bool from_scratch) {
+
+        _cleanup_(fdisk_unref_tablep) struct fdisk_table *original_table = NULL;
+        int capable, r;
+        Partition *p;
+
+        assert(context);
+
+        if (arg_pretty > 0 ||
+            (arg_pretty < 0 && isatty(STDOUT_FILENO) > 0)) {
+
+                if (context->n_partitions == 0)
+                        puts("Empty partition table.");
+                else
+                        (void) context_dump_partitions(context, node);
+
+                putc('\n', stdout);
+
+                (void) context_dump_partition_bar(context, node);
+                putc('\n', stdout);
+                fflush(stdout);
+        }
+
+        if (!from_scratch && !context_changed(context)) {
+                log_info("No changes.");
+                return 0;
+        }
+
+        if (arg_dry_run) {
+                log_notice("Refusing to repartition, please re-run with --dry-run=no.");
+                return 0;
+        }
+
+        log_info("Applying changes.");
+
+        if (from_scratch) {
+                r = context_discard_range(context, 0, context->total);
+                if (r == -EOPNOTSUPP)
+                        log_info("Storage does not support discarding, not discarding entire block device data.");
+                else if (r < 0)
+                        return log_error_errno(r, "Failed to discard entire block device: %m");
+                else if (r > 0)
+                        log_info("Discarded entire block device.");
+        }
+
+        r = fdisk_get_partitions(context->fdisk_context, &original_table);
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire partition table: %m");
+
+        /* Wipe fs signatures and discard sectors where the new partitions are going to be placed and in the
+         * gaps between partitions, just to be sure. */
+        r = context_wipe_and_discard(context, from_scratch);
+        if (r < 0)
+                return r;
+
+        LIST_FOREACH(partitions, p, context->partitions) {
+                if (p->dropped)
+                        continue;
+
+                assert(p->new_size != UINT64_MAX);
+                assert(p->offset != UINT64_MAX);
+                assert(p->partno != UINT64_MAX);
+
+                if (PARTITION_EXISTS(p)) {
+                        bool changed = false;
+
+                        assert(p->current_partition);
+
+                        if (p->new_size != p->current_size) {
+                                assert(p->new_size >= p->current_size);
+                                assert(p->new_size % 512 == 0);
+
+                                r = fdisk_partition_size_explicit(p->current_partition, true);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to enable explicit sizing: %m");
+
+                                r = fdisk_partition_set_size(p->current_partition, p->new_size / 512);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to grow partition: %m");
+
+                                log_info("Growing existing partition %" PRIu64 ".", p->partno);
+                                changed = true;
+                        }
+
+                        if (!sd_id128_equal(p->new_uuid, p->current_uuid)) {
+                                char buf[ID128_UUID_STRING_MAX];
+
+                                assert(!sd_id128_is_null(p->new_uuid));
+
+                                r = fdisk_partition_set_uuid(p->current_partition, id128_to_uuid_string(p->new_uuid, buf));
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to set partition UUID: %m");
+
+                                log_info("Initializing UUID of existing partition %" PRIu64 ".", p->partno);
+                                changed = true;
+                        }
+
+                        if (!streq_ptr(p->new_label, p->current_label)) {
+                                assert(!isempty(p->new_label));
+
+                                r = fdisk_partition_set_name(p->current_partition, p->new_label);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to set partition label: %m");
+
+                                log_info("Setting partition label of existing partition %" PRIu64 ".", p->partno);
+                                changed = true;
+                        }
+
+                        if (changed) {
+                                assert(!PARTITION_IS_FOREIGN(p)); /* never touch foreign partitions */
+
+                                r = fdisk_set_partition(context->fdisk_context, p->partno, p->current_partition);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to update partition: %m");
+                        }
+                } else {
+                        _cleanup_(fdisk_unref_partitionp) struct fdisk_partition *q = NULL;
+                        _cleanup_(fdisk_unref_parttypep) struct fdisk_parttype *t = NULL;
+                        char ids[ID128_UUID_STRING_MAX];
+
+                        assert(!p->new_partition);
+                        assert(p->offset % 512 == 0);
+                        assert(p->new_size % 512 == 0);
+                        assert(!sd_id128_is_null(p->new_uuid));
+                        assert(!isempty(p->new_label));
+
+                        t = fdisk_new_parttype();
+                        if (!t)
+                                return log_oom();
+
+                        r = fdisk_parttype_set_typestr(t, id128_to_uuid_string(p->type_uuid, ids));
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to initialize partition type: %m");
+
+                        q = fdisk_new_partition();
+                        if (!q)
+                                return log_oom();
+
+                        r = fdisk_partition_set_type(q, t);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set partition type: %m");
+
+                        r = fdisk_partition_size_explicit(q, true);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to enable explicit sizing: %m");
+
+                        r = fdisk_partition_set_start(q, p->offset / 512);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to position partition: %m");
+
+                        r = fdisk_partition_set_size(q, p->new_size / 512);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to grow partition: %m");
+
+                        r = fdisk_partition_set_partno(q, p->partno);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set partition number: %m");
+
+                        r = fdisk_partition_set_uuid(q, id128_to_uuid_string(p->new_uuid, ids));
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set partition UUID: %m");
+
+                        r = fdisk_partition_set_name(q, p->new_label);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set partition label: %m");
+
+                        log_info("Creating new partition %" PRIu64 ".", p->partno);
+
+                        r = fdisk_add_partition(context->fdisk_context, q, NULL);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to add partition: %m");
+
+                        assert(!p->new_partition);
+                        p->new_partition = TAKE_PTR(q);
+                }
+        }
+
+        log_info("Writing new partition table.");
+
+        r = fdisk_write_disklabel(context->fdisk_context);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write partition table: %m");
+
+        capable = device_kernel_partitions_supported(fdisk_get_devfd(context->fdisk_context));
+        if (capable < 0)
+                return capable;
+        if (capable > 0) {
+                log_info("Telling kernel to reread partition table.");
+
+                if (from_scratch)
+                        r = fdisk_reread_partition_table(context->fdisk_context);
+                else
+                        r = fdisk_reread_changes(context->fdisk_context, original_table);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to reread partition table: %m");
+        } else
+                log_notice("Not telling kernel to reread partition table, because selected image does not support kernel partition block devices.");
+
+        log_info("All done.");
+
+        return 0;
+}
+
+static int context_read_seed(Context *context, const char *root) {
+        int r;
+
+        assert(context);
+
+        if (!sd_id128_is_null(context->seed))
+                return 0;
+
+        if (!arg_randomize) {
+                _cleanup_close_ int fd = -1;
+
+                fd = chase_symlinks_and_open("/etc/machine-id", root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC, NULL);
+                if (fd == -ENOENT)
+                        log_info("No machine ID set, using randomized partition UUIDs.");
+                else if (fd < 0)
+                        return log_error_errno(fd, "Failed to determine machine ID of image: %m");
+                else {
+                        r = id128_read_fd(fd, ID128_PLAIN, &context->seed);
+                        if (r == -ENOMEDIUM)
+                                log_info("No machine ID set, using randomized partition UUIDs.");
+                        else if (r < 0)
+                                return log_error_errno(r, "Failed to parse machine ID of image: %m");
+
+                        return 0;
+                }
+        }
+
+        r = sd_id128_randomize(&context->seed);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate randomized seed: %m");
+
+        return 0;
+}
+
+static int context_factory_reset(Context *context, bool from_scratch) {
+        Partition *p;
+        size_t n = 0;
+        int r;
+
+        assert(context);
+
+        if (arg_factory_reset <= 0)
+                return 0;
+
+        if (from_scratch) /* Nothing to reset if we start from scratch */
+                return 0;
+
+        if (arg_dry_run) {
+                log_notice("Refusing to factory reset, please re-run with --dry-run=no.");
+                return 0;
+        }
+
+        log_info("Applying factory reset.");
+
+        LIST_FOREACH(partitions, p, context->partitions) {
+
+                if (!p->factory_reset || !PARTITION_EXISTS(p))
+                        continue;
+
+                assert(p->partno != UINT64_MAX);
+
+                log_info("Removing partition %" PRIu64 " for factory reset.", p->partno);
+
+                r = fdisk_delete_partition(context->fdisk_context, p->partno);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to remove partition %" PRIu64 ": %m", p->partno);
+
+                n++;
+        }
+
+        if (n == 0) {
+                log_info("Factory reset requested, but no partitions to delete found.");
+                return 0;
+        }
+
+        r = fdisk_write_disklabel(context->fdisk_context);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write disk label: %m");
+
+        log_info("Successfully deleted %zu partitions.", n);
+        return 1;
+}
+
+static int context_can_factory_reset(Context *context) {
+        Partition *p;
+
+        assert(context);
+
+        LIST_FOREACH(partitions, p, context->partitions)
+                if (p->factory_reset && PARTITION_EXISTS(p))
+                        return true;
+
+        return false;
+}
+
+static int help(void) {
+        _cleanup_free_ char *link = NULL;
+        int r;
+
+        r = terminal_urlify_man("systemd-repart", "1", &link);
+        if (r < 0)
+                return log_oom();
+
+        printf("%s [OPTIONS...] [DEVICE]\n"
+               "\n%sGrow and add partitions to partition table.%s\n\n"
+               "  -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"
+               "     --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"
+               "                          them\n"
+               "     --can-factory-reset  Test whether factory reset is defined\n"
+               "     --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"
+               "\nSee the %s for details.\n"
+               , program_invocation_short_name
+               , ansi_highlight(), ansi_normal()
+               , link
+        );
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_VERSION = 0x100,
+                ARG_DRY_RUN,
+                ARG_EMPTY,
+                ARG_DISCARD,
+                ARG_FACTORY_RESET,
+                ARG_CAN_FACTORY_RESET,
+                ARG_ROOT,
+                ARG_SEED,
+                ARG_PRETTY,
+                ARG_DEFINITIONS,
+        };
+
+        static const struct option options[] = {
+                { "help",              no_argument,       NULL, 'h'                   },
+                { "version",           no_argument,       NULL, ARG_VERSION           },
+                { "dry-run",           required_argument, NULL, ARG_DRY_RUN           },
+                { "empty",             required_argument, NULL, ARG_EMPTY             },
+                { "discard",           required_argument, NULL, ARG_DISCARD           },
+                { "factory-reset",     required_argument, NULL, ARG_FACTORY_RESET     },
+                { "can-factory-reset", no_argument,       NULL, ARG_CAN_FACTORY_RESET },
+                { "root",              required_argument, NULL, ARG_ROOT              },
+                { "seed",              required_argument, NULL, ARG_SEED              },
+                { "pretty",            required_argument, NULL, ARG_PRETTY            },
+                { "definitions",       required_argument, NULL, ARG_DEFINITIONS       },
+                {}
+        };
+
+        int c, r;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+
+                switch (c) {
+
+                case 'h':
+                        return help();
+
+                case ARG_VERSION:
+                        return version();
+
+                case ARG_DRY_RUN:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --dry-run= parameter: %s", optarg);
+
+                        arg_dry_run = r;
+                        break;
+
+                case ARG_EMPTY:
+                        if (isempty(optarg) || streq(optarg, "refuse"))
+                                arg_empty = EMPTY_REFUSE;
+                        else if (streq(optarg, "allow"))
+                                arg_empty = EMPTY_ALLOW;
+                        else if (streq(optarg, "require"))
+                                arg_empty = EMPTY_REQUIRE;
+                        else if (streq(optarg, "force"))
+                                arg_empty = EMPTY_FORCE;
+                        else
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "Failed to parse --empty= parameter: %s", optarg);
+                        break;
+
+                case ARG_DISCARD:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --discard= parameter: %s", optarg);
+
+                        arg_discard = r;
+                        break;
+
+                case ARG_FACTORY_RESET:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --factory-reset= parameter: %s", optarg);
+
+                        arg_factory_reset = r;
+                        break;
+
+                case ARG_CAN_FACTORY_RESET:
+                        arg_can_factory_reset = true;
+                        break;
+
+                case ARG_ROOT:
+                        r = parse_path_argument_and_warn(optarg, false, &arg_root);
+                        if (r < 0)
+                                return r;
+                        break;
+
+                case ARG_SEED:
+                        if (isempty(optarg)) {
+                                arg_seed = SD_ID128_NULL;
+                                arg_randomize = false;
+                        } else if (streq(optarg, "random"))
+                                arg_randomize = true;
+                        else {
+                                r = sd_id128_from_string(optarg, &arg_seed);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse seed: %s", optarg);
+
+                                arg_randomize = false;
+                        }
+
+                        break;
+
+                case ARG_PRETTY:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --pretty= parameter: %s", optarg);
+
+                        arg_pretty = r;
+                        break;
+
+                case ARG_DEFINITIONS:
+                        r = parse_path_argument_and_warn(optarg, false, &arg_definitions);
+                        if (r < 0)
+                                return r;
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        assert_not_reached("Unhandled option");
+                }
+
+        if (argc - optind > 1)
+                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))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Combination of --factory-reset=yes and --empty=force/--empty=require is invalid.");
+
+        if (arg_can_factory_reset)
+                arg_dry_run = true;
+
+        arg_node = argc > optind ? argv[optind] : NULL;
+        return 1;
+}
+
+static int parse_proc_cmdline_factory_reset(void) {
+        bool b;
+        int r;
+
+        if (arg_factory_reset >= 0) /* Never override what is specified on the process command line */
+                return 0;
+
+        if (!in_initrd()) /* Never honour kernel command line factory reset request outside of the initrd */
+                return 0;
+
+        r = proc_cmdline_get_bool("systemd.factory_reset", &b);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse systemd.factory_reset kernel command line argument: %m");
+        if (r > 0) {
+                arg_factory_reset = b;
+
+                if (b)
+                        log_notice("Honouring factory reset requested via kernel command line.");
+        }
+
+        return 0;
+}
+
+static int parse_efi_variable_factory_reset(void) {
+        _cleanup_free_ char *value = NULL;
+        int r;
+
+        if (arg_factory_reset >= 0) /* Never override what is specified on the process command line */
+                return 0;
+
+        if (!in_initrd()) /* Never honour EFI variable factory reset request outside of the initrd */
+                return 0;
+
+        r = efi_get_variable_string(EFI_VENDOR_SYSTEMD, "FactoryReset", &value);
+        if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
+                return 0;
+        if (r < 0)
+                return log_error_errno(r, "Failed to read EFI variable FactoryReset: %m");
+
+        r = parse_boolean(value);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse EFI variable FactoryReset: %m");
+
+        arg_factory_reset = r;
+        if (r)
+                log_notice("Honouring factory reset requested via EFI variable FactoryReset: %m");
+
+        return 0;
+}
+
+static int remove_efi_variable_factory_reset(void) {
+        int r;
+
+        r = efi_set_variable(EFI_VENDOR_SYSTEMD, "FactoryReset", NULL, 0);
+        if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
+                return 0;
+        if (r < 0)
+                return log_error_errno(r, "Failed to remove EFI variable FactoryReset: %m");
+
+        log_info("Successfully unset EFI variable FactoryReset.");
+        return 0;
+}
+
+static int acquire_root_devno(const char *p, int mode, char **ret) {
+        _cleanup_close_ int fd = -1;
+        struct stat st;
+        dev_t devno;
+        int r;
+
+        fd = open(p, mode);
+        if (fd < 0)
+                return -errno;
+
+        if (fstat(fd, &st) < 0)
+                return -errno;
+
+        if (S_ISREG(st.st_mode)) {
+                char *s;
+
+                s = strdup(p);
+                if (!s)
+                        return log_oom();
+
+                *ret = s;
+                return 0;
+        }
+
+        if (S_ISBLK(st.st_mode))
+                devno = st.st_rdev;
+        else if (S_ISDIR(st.st_mode)) {
+
+                devno = st.st_dev;
+
+                if (major(st.st_dev) == 0) {
+                        r = btrfs_get_block_device_fd(fd, &devno);
+                        if (r == -ENOTTY) /* not btrfs */
+                                return -ENODEV;
+                        if (r < 0)
+                                return r;
+                }
+
+        } else
+                return -ENOTBLK;
+
+        /* From dm-crypt to backing partition */
+        r = block_get_originating(devno, &devno);
+        if (r < 0)
+                log_debug_errno(r, "Failed to find underlying block device for '%s', ignoring: %m", p);
+
+        /* 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);
+
+        return device_path_make_canonical(S_IFBLK, devno, ret);
+}
+
+static int find_root(char **ret) {
+        const char *t;
+        int r;
+
+        if (arg_node) {
+                r = acquire_root_devno(arg_node, O_RDONLY|O_CLOEXEC, ret);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to determine backing device of %s: %m", arg_node);
+
+                return 0;
+        }
+
+        /* 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) */
+
+        FOREACH_STRING(t, "/", "/usr") {
+                _cleanup_free_ char *j = NULL;
+                const char *p;
+
+                if (in_initrd()) {
+                        j = path_join("/sysroot", t);
+                        if (!j)
+                                return log_oom();
+
+                        p = j;
+                } else
+                        p = t;
+
+                r = acquire_root_devno(p, O_RDONLY|O_DIRECTORY|O_CLOEXEC, ret);
+                if (r < 0) {
+                        if (r != -ENODEV)
+                                return log_error_errno(r, "Failed to determine backing device of %s: %m", p);
+                } else
+                        return 0;
+        }
+
+        return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "Failed to discover root block device.");
+}
+
+static int run(int argc, char *argv[]) {
+        _cleanup_(context_freep) Context* context = NULL;
+        _cleanup_free_ char *node = NULL;
+        bool from_scratch;
+        int r;
+
+        log_show_color(true);
+        log_parse_environment();
+        log_open();
+
+        if (in_initrd()) {
+                /* Default to operation on /sysroot when invoked in the initrd! */
+                arg_root = strdup("/sysroot");
+                if (!arg_root)
+                        return log_oom();
+        }
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r;
+
+        r = parse_proc_cmdline_factory_reset();
+        if (r < 0)
+                return r;
+
+        r = parse_efi_variable_factory_reset();
+        if (r < 0)
+                return r;
+
+        context = context_new(arg_seed);
+        if (!context)
+                return log_oom();
+
+        r = context_read_definitions(context, arg_definitions, arg_root);
+        if (r < 0)
+                return r;
+
+        if (context->n_partitions <= 0 && arg_empty != EMPTY_FORCE)
+                return 0;
+
+        r = find_root(&node);
+        if (r < 0)
+                return r;
+
+        r = context_load_partition_table(context, node);
+        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. */
+        if (r < 0)
+                return r;
+        from_scratch = r > 0; /* Starting from scratch */
+
+        if (arg_can_factory_reset) {
+                r = context_can_factory_reset(context);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        return EXIT_FAILURE;
+
+                return 0;
+        }
+
+        r = context_factory_reset(context, from_scratch);
+        if (r < 0)
+                return r;
+        if (r > 0) {
+                /* We actually did a factory reset! */
+                r = remove_efi_variable_factory_reset();
+                if (r < 0)
+                        return r;
+
+                /* Reload the reduced partition table */
+                context_unload_partition_table(context);
+                r = context_load_partition_table(context, node);
+                if (r < 0)
+                        return r;
+        }
+
+#if 0
+        (void) context_dump_partitions(context, node);
+        putchar('\n');
+#endif
+
+        r = context_read_seed(context, arg_root);
+        if (r < 0)
+                return r;
+
+        /* First try to fit new partitions in, dropping by priority until it fits */
+        for (;;) {
+                if (context_allocate_partitions(context))
+                        break; /* Success! */
+
+                if (!context_drop_one_priority(context))
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
+                                               "Can't fit requested partitions into free space, refusing.");
+        }
+
+        /* Now assign free space according to the weight logic */
+        r = context_grow_partitions(context);
+        if (r < 0)
+                return r;
+
+        /* Now calculate where each partition gets placed */
+        context_place_partitions(context);
+
+        /* Make sure each partition has a unique UUID and unique label */
+        r = context_acquire_partition_uuids_and_labels(context);
+        if (r < 0)
+                return r;
+
+        r = context_write_partition_table(context, node, from_scratch);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
index 7a86398a4b5d8fe6c1edc12dd9a1544729e881f7..e18826ab268538312fd3c0a96d426d8ad86c5338 100644 (file)
@@ -387,7 +387,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, &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);
                 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)
index 1fa6dc48c11464f440d55becf652c0a4cedd02d5..bf5badd699c3b29979e53eeb8f319de56abc2dd9 100644 (file)
@@ -7,7 +7,9 @@
 
 #include "alloc-util.h"
 #include "bus-error.h"
+#include "bus-unit-util.h"
 #include "bus-util.h"
+#include "bus-wait-for-jobs.h"
 #include "def.h"
 #include "dirent-util.h"
 #include "env-file.h"
@@ -39,6 +41,9 @@ static bool arg_reload = true;
 static bool arg_cat = false;
 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
 static const char *arg_host = NULL;
+static bool arg_enable = false;
+static bool arg_now = false;
+static bool arg_no_block = false;
 
 static int determine_image(const char *image, bool permit_non_existing, char **ret) {
         int r;
@@ -388,6 +393,234 @@ static int print_changes(sd_bus_message *m) {
         return 0;
 }
 
+static int maybe_enable_disable(sd_bus *bus, const char *path, bool enable) {
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_strv_free_ char **names = NULL;
+        UnitFileChange *changes = NULL;
+        size_t n_changes = 0;
+        int r;
+
+        if (!arg_enable)
+                return 0;
+
+        names = strv_new(path, NULL);
+        if (!names)
+                return log_oom();
+
+        r = sd_bus_message_new_method_call(
+                bus,
+                &m,
+                "org.freedesktop.systemd1",
+                "/org/freedesktop/systemd1",
+                "org.freedesktop.systemd1.Manager",
+                enable ? "EnableUnitFiles" : "DisableUnitFiles");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append_strv(m, names);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append(m, "b", arg_runtime);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        if (enable) {
+                r = sd_bus_message_append(m, "b", false);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
+
+        r = sd_bus_call(bus, m, 0, &error, &reply);
+        if (r < 0)
+                return log_error_errno(r, "Failed to %s the portable service %s: %s",
+                        enable ? "enable" : "disable", path, bus_error_message(&error, r));
+
+        if (enable) {
+                r = sd_bus_message_skip(reply, "b");
+                if (r < 0)
+                        return bus_log_parse_error(r);
+        }
+        (void) bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, &changes, &n_changes);
+
+        return 0;
+}
+
+static int maybe_start_stop(sd_bus *bus, const char *path, bool start, BusWaitForJobs *wait) {
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        char *name = (char *)basename(path), *job = NULL;
+        int r;
+
+        if (!arg_now)
+                return 0;
+
+        r = sd_bus_call_method(
+                        bus,
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        start ? "StartUnit" : "StopUnit",
+                        &error,
+                        &reply,
+                        "ss", name, "replace");
+        if (r < 0)
+                return log_error_errno(r, "Failed to %s the portable service %s: %s",
+                                       start ? "start" : "stop",
+                                       path,
+                                       bus_error_message(&error, r));
+
+        r = sd_bus_message_read(reply, "o", &job);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        if (!arg_quiet)
+                log_info("Queued %s to %s portable service %s.", job, start ? "start" : "stop", name);
+
+        if (wait) {
+                r = bus_wait_for_jobs_add(wait, job);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to watch %s job for %s %s: %m",
+                                               job, start ? "starting" : "stopping", name);
+        }
+
+        return 0;
+}
+
+static int maybe_enable_start(sd_bus *bus, sd_bus_message *reply) {
+        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *wait = NULL;
+        int r;
+
+        if (!arg_enable && !arg_now)
+                return 0;
+
+        if (!arg_no_block) {
+                r = bus_wait_for_jobs_new(bus, &wait);
+                if (r < 0)
+                        return log_error_errno(r, "Could not watch jobs: %m");
+        }
+
+        r = sd_bus_message_rewind(reply, true);
+        if (r < 0)
+                return r;
+        r = sd_bus_message_enter_container(reply, 'a', "(sss)");
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        for (;;) {
+                char *type, *path, *source;
+
+                r = sd_bus_message_read(reply, "(sss)", &type, &path, &source);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+                if (r == 0)
+                        break;
+
+                if (STR_IN_SET(type, "symlink", "copy") && ENDSWITH_SET(path, ".service", ".target", ".socket")) {
+                        (void) maybe_enable_disable(bus, path, true);
+                        (void) maybe_start_stop(bus, path, true, wait);
+                }
+        }
+
+        r = sd_bus_message_exit_container(reply);
+        if (r < 0)
+                return r;
+
+        if (!arg_no_block) {
+                r = bus_wait_for_jobs(wait, arg_quiet, NULL);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int maybe_stop_disable(sd_bus *bus, char *image, char *argv[]) {
+        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *wait = NULL;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_strv_free_ char **matches = NULL;
+        int r;
+
+        if (!arg_enable && !arg_now)
+                return 0;
+
+        r = determine_matches(argv[1], argv + 2, true, &matches);
+        if (r < 0)
+                return r;
+
+        r = bus_wait_for_jobs_new(bus, &wait);
+        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");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append(m, "s", image);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append_strv(m, matches);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_call(bus, m, 0, &error, &reply);
+        if (r < 0)
+                return log_error_errno(r, "Failed to inspect image metadata: %s", bus_error_message(&error, r));
+
+        r = sd_bus_message_skip(reply, "say");
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        r = sd_bus_message_enter_container(reply, 'a', "{say}");
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        for (;;) {
+                const char *name;
+
+                r = sd_bus_message_enter_container(reply, 'e', "say");
+                if (r < 0)
+                        return bus_log_parse_error(r);
+                if (r == 0)
+                        break;
+
+                r = sd_bus_message_read(reply, "s", &name);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                r = sd_bus_message_skip(reply, "ay");
+                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);
+
+                (void) maybe_start_stop(bus, name, false, wait);
+                (void) maybe_enable_disable(bus, name, false);
+        }
+
+        r = sd_bus_message_exit_container(reply);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        /* Stopping must always block or the detach will fail if the unit is still running */
+        r = bus_wait_for_jobs(wait, arg_quiet, NULL);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
 static int attach_image(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -439,6 +672,9 @@ static int attach_image(int argc, char *argv[], void *userdata) {
         (void) maybe_reload(&bus);
 
         print_changes(reply);
+
+        (void) maybe_enable_start(bus, reply);
+
         return 0;
 }
 
@@ -459,6 +695,8 @@ static int detach_image(int argc, char *argv[], void *userdata) {
 
         (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
+        (void) maybe_stop_disable(bus, image, argv);
+
         r = sd_bus_call_method(
                         bus,
                         "org.freedesktop.portable1",
@@ -764,7 +1002,8 @@ static int help(int argc, char *argv[], void *userdata) {
                "  list                        List available portable service images\n"
                "  attach NAME|PATH [PREFIX...]\n"
                "                              Attach the specified portable service image\n"
-               "  detach NAME|PATH            Detach the specified portable service image\n"
+               "  detach NAME|PATH [PREFIX...]\n"
+               "                              Detach the specified portable service image\n"
                "  inspect NAME|PATH [PREFIX...]\n"
                "                              Show details of specified portable service image\n"
                "  is-attached NAME|PATH       Query if portable service image is attached\n"
@@ -786,6 +1025,11 @@ static int help(int argc, char *argv[], void *userdata) {
                "     --no-reload              Don't reload the system and service manager\n"
                "     --cat                    When inspecting include unit and os-release file\n"
                "                              contents\n"
+               "     --enable                 Immediately enable/disable the portable service\n"
+               "                              after attach/detach\n"
+               "     --now                    Immediately start/stop the portable service after\n"
+               "                              attach/before detach\n"
+               "     --no-block               Don't block waiting for attach --now to complete\n"
                "\nSee the %s for details.\n"
                , program_invocation_short_name
                , ansi_highlight()
@@ -807,6 +1051,9 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_RUNTIME,
                 ARG_NO_RELOAD,
                 ARG_CAT,
+                ARG_ENABLE,
+                ARG_NOW,
+                ARG_NO_BLOCK,
         };
 
         static const struct option options[] = {
@@ -823,6 +1070,9 @@ static int parse_argv(int argc, char *argv[]) {
                 { "runtime",         no_argument,       NULL, ARG_RUNTIME         },
                 { "no-reload",       no_argument,       NULL, ARG_NO_RELOAD       },
                 { "cat",             no_argument,       NULL, ARG_CAT             },
+                { "enable",          no_argument,       NULL, ARG_ENABLE          },
+                { "now",             no_argument,       NULL, ARG_NOW             },
+                { "no-block",        no_argument,       NULL, ARG_NO_BLOCK        },
                 {}
         };
 
@@ -909,6 +1159,18 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_cat = true;
                         break;
 
+                case ARG_ENABLE:
+                        arg_enable = true;
+                        break;
+
+                case ARG_NOW:
+                        arg_now = true;
+                        break;
+
+                case ARG_NO_BLOCK:
+                        arg_no_block = true;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -925,7 +1187,7 @@ static int run(int argc, char *argv[]) {
                 { "help",        VERB_ANY, VERB_ANY, 0,            help              },
                 { "list",        VERB_ANY, 1,        VERB_DEFAULT, list_images       },
                 { "attach",      2,        VERB_ANY, 0,            attach_image      },
-                { "detach",      2,        2,        0,            detach_image      },
+                { "detach",      2,        VERB_ANY, 0,            detach_image      },
                 { "inspect",     2,        VERB_ANY, 0,            inspect_image     },
                 { "is-attached", 2,        2,        0,            is_image_attached },
                 { "read-only",   2,        3,        0,            read_only_image   },
index 89168c3c43bab79bb1c6ec0ae84ec94c088f5d4c..0fa05434efb34094325b808d7f3be1e344f08221 100644 (file)
@@ -3,7 +3,7 @@
 #include "alloc-util.h"
 #include "btrfs-util.h"
 #include "bus-common-errors.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
 #include "fd-util.h"
 #include "io-util.h"
 #include "machine-image.h"
index fd2b7c99944a062565bd7add3b0b800e8c7b7c8f..2bd1c495e4e0ee540202780230480a74be27886a 100644 (file)
@@ -8,6 +8,7 @@
 #include "alloc-util.h"
 #include "bus-common-errors.h"
 #include "bus-label.h"
+#include "bus-polkit.h"
 #include "bus-util.h"
 #include "fd-util.h"
 #include "fileio.h"
index c74ec429627b721a6b8d85d857437d5eea2cbf2e..75b76926e5565be0e0b4370d17ff6cfcb59e6bb7 100644 (file)
@@ -7,7 +7,7 @@
 #include "sd-daemon.h"
 
 #include "alloc-util.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
 #include "def.h"
 #include "main-func.h"
 #include "portabled-bus.h"
index 2fcbcb281ab7255aa000796319d875a27e6a56bb..596bff98f1478bd65e303db5db79ba47aafc6a55 100644 (file)
@@ -19,6 +19,7 @@
 #include "io-util.h"
 #include "log.h"
 #include "main-func.h"
+#include "missing_random.h"
 #include "missing_syscall.h"
 #include "mkdir.h"
 #include "parse-util.h"
index df40349e6622d312bbb033f5749cf1441b147978..f20e8c44b8bc3bf512dd64073ba8b9dad27c1600 100644 (file)
@@ -14,6 +14,7 @@
 #include "bus-util.h"
 #include "dns-domain.h"
 #include "escape.h"
+#include "format-table.h"
 #include "format-util.h"
 #include "gcrypt-util.h"
 #include "main-func.h"
@@ -984,6 +985,7 @@ static int verb_tlsa(int argc, char **argv, void *userdata) {
 static int show_statistics(int argc, char **argv, void *userdata) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_(table_unrefp) Table *table = NULL;
         sd_bus *bus = userdata;
         uint64_t n_current_transactions, n_total_transactions,
                 cache_size, n_cache_hit, n_cache_miss,
@@ -1025,14 +1027,6 @@ static int show_statistics(int argc, char **argv, void *userdata) {
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        printf("%sTransactions%s\n"
-               "Current Transactions: %" PRIu64 "\n"
-               "  Total Transactions: %" PRIu64 "\n",
-               ansi_highlight(),
-               ansi_normal(),
-               n_current_transactions,
-               n_total_transactions);
-
         reply = sd_bus_message_unref(reply);
 
         r = sd_bus_get_property(bus,
@@ -1053,16 +1047,6 @@ static int show_statistics(int argc, char **argv, void *userdata) {
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        printf("\n%sCache%s\n"
-               "  Current Cache Size: %" PRIu64 "\n"
-               "          Cache Hits: %" PRIu64 "\n"
-               "        Cache Misses: %" PRIu64 "\n",
-               ansi_highlight(),
-               ansi_normal(),
-               cache_size,
-               n_cache_hit,
-               n_cache_miss);
-
         reply = sd_bus_message_unref(reply);
 
         r = sd_bus_get_property(bus,
@@ -1084,17 +1068,53 @@ static int show_statistics(int argc, char **argv, void *userdata) {
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        printf("\n%sDNSSEC Verdicts%s\n"
-               "              Secure: %" PRIu64 "\n"
-               "            Insecure: %" PRIu64 "\n"
-               "               Bogus: %" PRIu64 "\n"
-               "       Indeterminate: %" PRIu64 "\n",
-               ansi_highlight(),
-               ansi_normal(),
-               n_dnssec_secure,
-               n_dnssec_insecure,
-               n_dnssec_bogus,
-               n_dnssec_indeterminate);
+        table = table_new("key", "value");
+        if (!table)
+                return log_oom();
+
+        table_set_header(table, false);
+
+        r = table_add_many(table,
+                           TABLE_STRING, "Transactions",
+                           TABLE_SET_COLOR, ansi_highlight(),
+                           TABLE_EMPTY,
+                           TABLE_STRING, "Current Transactions:",
+                           TABLE_SET_ALIGN_PERCENT, 100,
+                           TABLE_UINT64, n_current_transactions,
+                           TABLE_STRING, "Total Transactions:",
+                           TABLE_UINT64, n_total_transactions,
+                           TABLE_EMPTY, TABLE_EMPTY,
+                           TABLE_STRING, "Cache",
+                           TABLE_SET_COLOR, ansi_highlight(),
+                           TABLE_SET_ALIGN_PERCENT, 0,
+                           TABLE_EMPTY,
+                           TABLE_STRING, "Current Cache Size:",
+                           TABLE_SET_ALIGN_PERCENT, 100,
+                           TABLE_UINT64, cache_size,
+                           TABLE_STRING, "Cache Hits:",
+                           TABLE_UINT64, n_cache_hit,
+                           TABLE_STRING, "Cache Misses:",
+                           TABLE_UINT64, n_cache_miss,
+                           TABLE_EMPTY, TABLE_EMPTY,
+                           TABLE_STRING, "DNSSEC Verdicts",
+                           TABLE_SET_COLOR, ansi_highlight(),
+                           TABLE_SET_ALIGN_PERCENT, 0,
+                           TABLE_EMPTY,
+                           TABLE_STRING, "Secure:",
+                           TABLE_SET_ALIGN_PERCENT, 100,
+                           TABLE_UINT64, n_dnssec_secure,
+                           TABLE_STRING, "Insecure:",
+                           TABLE_UINT64, n_dnssec_insecure,
+                           TABLE_STRING, "Bogus:",
+                           TABLE_UINT64, n_dnssec_bogus,
+                           TABLE_STRING, "Indeterminate:",
+                           TABLE_UINT64, n_dnssec_indeterminate);
+        if (r < 0)
+                table_log_add_error(r);
+
+        r = table_print(table, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to print table: %m");
 
         return 0;
 }
@@ -1365,6 +1385,21 @@ static void link_info_clear(struct link_info *p) {
         strv_free(p->ntas);
 }
 
+static int dump_list(Table *table, const char *prefix, char * const *l) {
+        int r;
+
+        if (strv_isempty(l))
+                return 0;
+
+        r = table_add_many(table,
+                           TABLE_STRING, prefix,
+                           TABLE_STRV, l);
+        if (r < 0)
+                return table_log_add_error(r);
+
+        return 0;
+}
+
 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)      },
@@ -1383,9 +1418,9 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
         _cleanup_(link_info_clear) struct link_info link_info = {};
+        _cleanup_(table_unrefp) Table *table = NULL;
         _cleanup_free_ char *p = NULL;
         char ifi[DECIMAL_STR_MAX(int)], ifname[IF_NAMESIZE + 1] = "";
-        char **i;
         int r;
 
         assert(bus);
@@ -1471,49 +1506,80 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
         printf("%sLink %i (%s)%s\n",
                ansi_highlight(), ifindex, name, ansi_normal());
 
+        table = table_new("key", "value");
+        if (!table)
+                return log_oom();
+
+        table_set_header(table, false);
+
+        r = table_add_many(table,
+                           TABLE_STRING, "Current Scopes:",
+                           TABLE_SET_ALIGN_PERCENT, 100);
+        if (r < 0)
+                return table_log_add_error(r);
+
         if (link_info.scopes_mask == 0)
-                printf("      Current Scopes: none\n");
-        else
-                printf("      Current Scopes:%s%s%s%s%s\n",
-                       link_info.scopes_mask & SD_RESOLVED_DNS ? " DNS" : "",
-                       link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "",
-                       link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "",
-                       link_info.scopes_mask & SD_RESOLVED_MDNS_IPV4 ? " mDNS/IPv4" : "",
-                       link_info.scopes_mask & SD_RESOLVED_MDNS_IPV6 ? " mDNS/IPv6" : "");
-
-        printf("DefaultRoute setting: %s\n"
-               "       LLMNR setting: %s\n"
-               "MulticastDNS setting: %s\n"
-               "  DNSOverTLS setting: %s\n"
-               "      DNSSEC setting: %s\n"
-               "    DNSSEC supported: %s\n",
-               yes_no(link_info.default_route),
-               strna(link_info.llmnr),
-               strna(link_info.mdns),
-               strna(link_info.dns_over_tls),
-               strna(link_info.dnssec),
-               yes_no(link_info.dnssec_supported));
-
-        if (link_info.current_dns)
-                printf("  Current DNS Server: %s\n", link_info.current_dns);
-
-        STRV_FOREACH(i, link_info.dns) {
-                printf("         %s %s\n",
-                       i == link_info.dns ? "DNS Servers:" : "            ",
-                       *i);
-        }
-
-        STRV_FOREACH(i, link_info.domains) {
-                printf("          %s %s\n",
-                       i == link_info.domains ? "DNS Domain:" : "           ",
-                       *i);
-        }
-
-        STRV_FOREACH(i, link_info.ntas) {
-                printf("          %s %s\n",
-                       i == link_info.ntas ? "DNSSEC NTA:" : "           ",
-                       *i);
+                r = table_add_cell(table, NULL, TABLE_STRING, "none");
+        else {
+                _cleanup_free_ char *buf = NULL;
+                size_t len;
+
+                if (asprintf(&buf, "%s%s%s%s%s",
+                             link_info.scopes_mask & SD_RESOLVED_DNS ? "DNS " : "",
+                             link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV4 ? "LLMNR/IPv4 " : "",
+                             link_info.scopes_mask & SD_RESOLVED_LLMNR_IPV6 ? "LLMNR/IPv6 " : "",
+                             link_info.scopes_mask & SD_RESOLVED_MDNS_IPV4 ? "mDNS/IPv4 " : "",
+                             link_info.scopes_mask & SD_RESOLVED_MDNS_IPV6 ? "mDNS/IPv6 " : "") < 0)
+                        return log_oom();
+
+                len = strlen(buf);
+                assert(len > 0);
+                buf[len - 1] = '\0';
+
+                r = table_add_cell(table, NULL, TABLE_STRING, buf);
         }
+        if (r < 0)
+                return table_log_add_error(r);
+
+        r = table_add_many(table,
+                           TABLE_STRING, "DefaultRoute setting:",
+                           TABLE_BOOLEAN, link_info.default_route,
+                           TABLE_STRING, "LLMNR setting:",
+                           TABLE_STRING, strna(link_info.llmnr),
+                           TABLE_STRING, "MulticastDNS setting:",
+                           TABLE_STRING, strna(link_info.mdns),
+                           TABLE_STRING, "DNSOverTLS setting:",
+                           TABLE_STRING, strna(link_info.dns_over_tls),
+                           TABLE_STRING, "DNSSEC setting:",
+                           TABLE_STRING, strna(link_info.dnssec),
+                           TABLE_STRING, "DNSSEC supported:",
+                           TABLE_BOOLEAN, link_info.dnssec_supported);
+        if (r < 0)
+                return table_log_add_error(r);
+
+        if (link_info.current_dns) {
+                r = table_add_many(table,
+                                   TABLE_STRING, "Current DNS Server:",
+                                   TABLE_STRING, link_info.current_dns);
+                if (r < 0)
+                        return table_log_add_error(r);
+        }
+
+        r = dump_list(table, "DNS Servers:", link_info.dns);
+        if (r < 0)
+                return r;
+
+        r = dump_list(table, "DNS Domain:", link_info.domains);
+        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");
 
         if (empty_line)
                 *empty_line = true;
@@ -1653,7 +1719,7 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
         _cleanup_(global_info_clear) struct global_info global_info = {};
-        char **i;
+        _cleanup_(table_unrefp) Table *table = NULL;
         int r;
 
         assert(bus);
@@ -1711,44 +1777,55 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
 
         printf("%sGlobal%s\n", ansi_highlight(), ansi_normal());
 
-        printf("       LLMNR setting: %s\n"
-               "MulticastDNS setting: %s\n"
-               "  DNSOverTLS setting: %s\n"
-               "      DNSSEC setting: %s\n"
-               "    DNSSEC supported: %s\n",
-               strna(global_info.llmnr),
-               strna(global_info.mdns),
-               strna(global_info.dns_over_tls),
-               strna(global_info.dnssec),
-               yes_no(global_info.dnssec_supported));
-
-        if (global_info.current_dns)
-                printf("  Current DNS Server: %s\n", global_info.current_dns);
+        table = table_new("key", "value");
+        if (!table)
+                return log_oom();
 
-        STRV_FOREACH(i, global_info.dns) {
-                printf("         %s %s\n",
-                       i == global_info.dns ? "DNS Servers:" : "            ",
-                       *i);
+        table_set_header(table, false);
+
+        r = table_add_many(table,
+                           TABLE_STRING, "LLMNR setting:",
+                           TABLE_SET_ALIGN_PERCENT, 100,
+                           TABLE_STRING, strna(global_info.llmnr),
+                           TABLE_STRING, "MulticastDNS setting:",
+                           TABLE_STRING, strna(global_info.mdns),
+                           TABLE_STRING, "DNSOverTLS setting:",
+                           TABLE_STRING, strna(global_info.dns_over_tls),
+                           TABLE_STRING, "DNSSEC setting:",
+                           TABLE_STRING, strna(global_info.dnssec),
+                           TABLE_STRING, "DNSSEC supported:",
+                           TABLE_BOOLEAN, global_info.dnssec_supported);
+        if (r < 0)
+                return table_log_add_error(r);
+
+        if (global_info.current_dns) {
+                r = table_add_many(table,
+                                   TABLE_STRING, "Current DNS Server:",
+                                   TABLE_STRING, global_info.current_dns);
+                if (r < 0)
+                        return table_log_add_error(r);
         }
 
-        STRV_FOREACH(i, global_info.fallback_dns) {
-                printf("%s %s\n",
-                       i == global_info.fallback_dns ? "Fallback DNS Servers:" : "                     ",
-                       *i);
-        }
+        r = dump_list(table, "DNS Servers:", global_info.dns);
+        if (r < 0)
+                return r;
 
-        STRV_FOREACH(i, global_info.domains) {
-                printf("          %s %s\n",
-                       i == global_info.domains ? "DNS Domain:" : "           ",
-                       *i);
-        }
+        r = dump_list(table, "Fallback DNS Servers:", global_info.fallback_dns);
+        if (r < 0)
+                return r;
+
+        r = dump_list(table, "DNS Domain:", global_info.domains);
+        if (r < 0)
+                return r;
 
         strv_sort(global_info.ntas);
-        STRV_FOREACH(i, global_info.ntas) {
-                printf("          %s %s\n",
-                       i == global_info.ntas ? "DNSSEC NTA:" : "           ",
-                       *i);
-        }
+        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");
 
         *empty_line = true;
 
index ff6acb2eac1fac0b14d54dde7b35f7a8fad07797..ffe61bdb9f5f85fe1d0645b926c380ebff65462b 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "alloc-util.h"
 #include "bus-common-errors.h"
+#include "bus-polkit.h"
 #include "bus-util.h"
 #include "dns-domain.h"
 #include "memory-util.h"
index 691f07945f8ec7abbf5aa2352582cb88ef0c8e79..d7e7b5a853ca3fb590fbca0b4a343048bffab806 100644 (file)
@@ -863,7 +863,7 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
 
         r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative);
         if (r < 0) {
-                log_debug_errno(r, "Failed to lookup key: %m");
+                log_debug_errno(r, "Failed to look up key: %m");
                 return;
         }
         if (r == 0)
index 89e48403f090223d128b2b1a77b41322ca1c1548..92842bcf89d43f8646b768f1b3333a8b88db4780 100644 (file)
 
 static const char trust_anchor_dirs[] = CONF_PATHS_NULSTR("dnssec-trust-anchors.d");
 
-/* The first DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved December 2015 */
-static const uint8_t root_digest1[] =
-        { 0x49, 0xAA, 0xC1, 0x1D, 0x7B, 0x6F, 0x64, 0x46, 0x70, 0x2E, 0x54, 0xA1, 0x60, 0x73, 0x71, 0x60,
-          0x7A, 0x1A, 0x41, 0x85, 0x52, 0x00, 0xFD, 0x2C, 0xE1, 0xCD, 0xDE, 0x32, 0xF2, 0x4E, 0x8F, 0xB5 };
-
 /* The second DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved February 2017 */
 static const uint8_t root_digest2[] =
         { 0xE0, 0x6D, 0x44, 0xB8, 0x0B, 0x8F, 0x1D, 0x39, 0xA9, 0x5C, 0x0B, 0x0D, 0x7C, 0x65, 0xD0, 0x84,
@@ -96,11 +91,7 @@ static int dns_trust_anchor_add_builtin_positive(DnsTrustAnchor *d) {
         if (!answer)
                 return -ENOMEM;
 
-        /* Add the two RRs from https://data.iana.org/root-anchors/root-anchors.xml */
-        r = add_root_ksk(answer, key, 19036, DNSSEC_ALGORITHM_RSASHA256, DNSSEC_DIGEST_SHA256, root_digest1, sizeof(root_digest1));
-        if (r < 0)
-                return r;
-
+        /* Add the currently valid RRs from https://data.iana.org/root-anchors/root-anchors.xml */
         r = add_root_ksk(answer, key, 20326, DNSSEC_ALGORITHM_RSASHA256, DNSSEC_DIGEST_SHA256, root_digest2, sizeof(root_digest2));
         if (r < 0)
                 return r;
index 24bb37b35e848eaaf59195267ad887046f94b508..f7dcb3bfa56f8fb4afc035e9ab165d3211129b21 100644 (file)
@@ -1,9 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include "alloc-util.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
 #include "missing_capability.h"
-#include "resolved-dnssd.h"
 #include "resolved-dnssd-bus.h"
+#include "resolved-dnssd.h"
 #include "resolved-link.h"
 #include "strv.h"
 #include "user-util.h"
index aad3bb4481f5e2bf1b03416892b9db23db0843ef..ef90a7d5ae941723b19a630dadf6f7c04a39ae94 100644 (file)
@@ -56,15 +56,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 ce0a4373715582e30e650450707ec4e48eb35a43..7763cbcb5acb31add7153b05a78f57cf3e4020bf 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <openssl/bio.h>
 #include <openssl/err.h>
+#include <openssl/x509v3.h>
 
 #include "io-util.h"
 #include "resolved-dns-stream.h"
@@ -73,18 +74,26 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
                 return -ENOMEM;
 
         SSL_set_connect_state(s);
-        SSL_set_session(s, server->dnstls_data.session);
+        r = SSL_set_session(s, server->dnstls_data.session);
+        if (r == 0)
+                return -EIO;
         SSL_set_bio(s, TAKE_PTR(rb), TAKE_PTR(wb));
 
         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)))
-                        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) {
@@ -106,8 +115,8 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
                         char errbuf[256];
 
                         ERR_error_string_n(error, errbuf, sizeof(errbuf));
-                        log_debug("Failed to invoke SSL_do_handshake: %s", errbuf);
-                        return -ECONNREFUSED;
+                        return log_debug_errno(SYNTHETIC_ERRNO(ECONNREFUSED),
+                                               "Failed to invoke SSL_do_handshake: %s", errbuf);
                 }
         }
 
@@ -368,20 +377,27 @@ void dnstls_server_free(DnsServer *server) {
 
 int dnstls_manager_init(Manager *manager) {
         int r;
+
         assert(manager);
 
         ERR_load_crypto_strings();
         SSL_load_error_strings();
-        manager->dnstls_data.ctx = SSL_CTX_new(TLS_client_method());
 
+        manager->dnstls_data.ctx = SSL_CTX_new(TLS_client_method());
         if (!manager->dnstls_data.ctx)
                 return -ENOMEM;
 
-        SSL_CTX_set_min_proto_version(manager->dnstls_data.ctx, TLS1_2_VERSION);
-        SSL_CTX_set_options(manager->dnstls_data.ctx, SSL_OP_NO_COMPRESSION);
+        r = SSL_CTX_set_min_proto_version(manager->dnstls_data.ctx, TLS1_2_VERSION);
+        if (r == 0)
+                return -EIO;
+
+        (void) SSL_CTX_set_options(manager->dnstls_data.ctx, SSL_OP_NO_COMPRESSION);
+
         r = SSL_CTX_set_default_verify_paths(manager->dnstls_data.ctx);
-        if (r < 0)
-                log_warning("Failed to load system trust store: %s", ERR_error_string(ERR_get_error(), NULL));
+        if (r == 0)
+                return log_warning_errno(SYNTHETIC_ERRNO(EIO),
+                                         "Failed to load system trust store: %s",
+                                         ERR_error_string(ERR_get_error(), NULL));
 
         return 0;
 }
index a8480f190a6f96b04b15e55f4c85acd4573477a3..2a166c11b04b65102b0a96bb5a97c4efb0870de5 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "alloc-util.h"
 #include "bus-common-errors.h"
+#include "bus-polkit.h"
 #include "bus-util.h"
 #include "parse-util.h"
 #include "resolve-util.h"
index af91a8ec1a5e7d0379b432652c4482c43aa3cf41..4f72077720b1ca8aad518d482759bdf961f7a56d 100644 (file)
@@ -14,7 +14,7 @@
 
 #include "af-list.h"
 #include "alloc-util.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
 #include "dirent-util.h"
 #include "dns-domain.h"
 #include "fd-util.h"
index eba2978991fa19ac8b0d44294c886320d9817846..921720febfedd63a5522f3a91eca38fb85c1ce4e 100644 (file)
@@ -195,7 +195,7 @@ static int mdns_scope_process_query(DnsScope *s, DnsPacket *p) {
 
                 r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative);
                 if (r < 0)
-                        return log_debug_errno(r, "Failed to lookup key: %m");
+                        return log_debug_errno(r, "Failed to look up key: %m");
 
                 if (tentative && DNS_PACKET_NSCOUNT(p) > 0) {
                         /*
index 7f6775f4a130c1c45ac04a050814022a2eab2438..6abcbfca9d00fc6a09832dbe18d56275ff63c259 100644 (file)
@@ -394,6 +394,10 @@ finish:
         return r;
 }
 
+#define NO_ECHO "(no echo) "
+#define PRESS_TAB "(press TAB for no echo) "
+#define SKIPPED "(skipped)"
+
 int ask_password_tty(
                 int ttyfd,
                 const char *message,
@@ -409,7 +413,7 @@ int ask_password_tty(
                 _POLL_MAX,
         };
 
-        bool reset_tty = false, dirty = false, use_color = false;
+        bool reset_tty = false, dirty = false, use_color = false, press_tab_visible = false;
         _cleanup_close_ int cttyfd = -1, notify = -1;
         struct termios old_termios, new_termios;
         char passphrase[LINE_MAX + 1] = {}, *x;
@@ -465,6 +469,13 @@ int ask_password_tty(
                 (void) loop_write(ttyfd, message, strlen(message), false);
                 (void) loop_write(ttyfd, " ", 1, false);
 
+                if (!(flags & ASK_PASSWORD_SILENT)) {
+                        if (use_color)
+                                (void) loop_write(ttyfd, ANSI_GREY, STRLEN(ANSI_GREY), false);
+                        (void) loop_write(ttyfd, PRESS_TAB, strlen(PRESS_TAB), false);
+                        press_tab_visible = true;
+                }
+
                 if (use_color)
                         (void) loop_write(ttyfd, ANSI_NORMAL, STRLEN(ANSI_NORMAL), false);
 
@@ -550,13 +561,19 @@ int ask_password_tty(
 
                 }
 
+                if (press_tab_visible) {
+                        assert(ttyfd >= 0);
+                        backspace_chars(ttyfd, strlen(PRESS_TAB));
+                        press_tab_visible = false;
+                }
+
                 /* We treat EOF, newline and NUL byte all as valid end markers */
                 if (n == 0 || c == '\n' || c == 0)
                         break;
 
                 if (c == 4) { /* C-d also known as EOT */
                         if (ttyfd >= 0)
-                                (void) loop_write(ttyfd, "(skipped)", 9, false);
+                                (void) loop_write(ttyfd, SKIPPED, strlen(SKIPPED), false);
 
                         goto skipped;
                 }
@@ -606,7 +623,7 @@ int ask_password_tty(
                                  * first key (and only as first key), or ... */
 
                                 if (ttyfd >= 0)
-                                        (void) loop_write(ttyfd, "(no echo) ", 10, false);
+                                        (void) loop_write(ttyfd, NO_ECHO, strlen(NO_ECHO), false);
 
                         } else if (ttyfd >= 0)
                                 (void) loop_write(ttyfd, "\a", 1, false);
@@ -619,7 +636,7 @@ int ask_password_tty(
                         /* ... or by pressing TAB at any time. */
 
                         if (ttyfd >= 0)
-                                (void) loop_write(ttyfd, "(no echo) ", 10, false);
+                                (void) loop_write(ttyfd, NO_ECHO, strlen(NO_ECHO), false);
 
                 } else if (p >= sizeof(passphrase)-1) {
 
@@ -658,11 +675,15 @@ int ask_password_tty(
                 goto finish;
 
 skipped:
-        if (keyname)
-                (void) add_to_keyring_and_log(keyname, flags, l);
-
-        *ret = TAKE_PTR(l);
-        r = 0;
+        if (strv_isempty(l))
+                r = log_debug_errno(SYNTHETIC_ERRNO(ECANCELED), "Password query was cancelled.");
+        else {
+                if (keyname)
+                        (void) add_to_keyring_and_log(keyname, flags, l);
+
+                *ret = TAKE_PTR(l);
+                r = 0;
+        }
 
 finish:
         if (ttyfd >= 0 && reset_tty) {
@@ -675,9 +696,10 @@ finish:
 
 static int create_socket(char **ret) {
         _cleanup_free_ char *path = NULL;
-        union sockaddr_union sa = {};
+        union sockaddr_union sa;
+        socklen_t sa_len;
         _cleanup_close_ int fd = -1;
-        int salen, r;
+        int r;
 
         assert(ret);
 
@@ -688,14 +710,14 @@ static int create_socket(char **ret) {
         if (asprintf(&path, "/run/systemd/ask-password/sck.%" PRIx64, random_u64()) < 0)
                 return -ENOMEM;
 
-        salen = sockaddr_un_set_path(&sa.un, path);
-        if (salen < 0)
-                return salen;
+        r = sockaddr_un_set_path(&sa.un, path);
+        if (r < 0)
+                return r;
+        sa_len = r;
 
-        RUN_WITH_UMASK(0177) {
-                if (bind(fd, &sa.sa, salen) < 0)
+        RUN_WITH_UMASK(0177)
+                if (bind(fd, &sa.sa, sa_len) < 0)
                         return -errno;
-        }
 
         r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true);
         if (r < 0)
index 699b101b3901688f05311decd72c2a7bf2edc6be..13d7b2f16075ae37cfea2d8e82d39d3e04d473eb 100644 (file)
@@ -164,6 +164,7 @@ void boot_config_free(BootConfig *config) {
         free(config->auto_entries);
         free(config->auto_firmware);
         free(config->console_mode);
+        free(config->random_seed_mode);
 
         free(config->entry_oneshot);
         free(config->entry_default);
@@ -229,6 +230,8 @@ static int boot_loader_read_conf(const char *path, BootConfig *config) {
                         r = free_and_strdup(&config->auto_firmware, p);
                 else if (streq(field, "console-mode"))
                         r = free_and_strdup(&config->console_mode, p);
+                else if (streq(field, "random-seed-mode"))
+                        r = free_and_strdup(&config->random_seed_mode, p);
                 else {
                         log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field);
                         continue;
index a825b35bc58c829caabb0036e5ea44e4b98c1a89..b40680b643b3b3ff39419ec763e9cbc016f09f44 100644 (file)
@@ -43,6 +43,7 @@ typedef struct BootConfig {
         char *auto_entries;
         char *auto_firmware;
         char *console_mode;
+        char *random_seed_mode;
 
         char *entry_oneshot;
         char *entry_default;
diff --git a/src/shared/bus-polkit.c b/src/shared/bus-polkit.c
new file mode 100644 (file)
index 0000000..9b0a455
--- /dev/null
@@ -0,0 +1,415 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "bus-polkit.h"
+#include "strv.h"
+#include "user-util.h"
+
+static int check_good_user(sd_bus_message *m, uid_t good_user) {
+        _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+        uid_t sender_uid;
+        int r;
+
+        assert(m);
+
+        if (good_user == UID_INVALID)
+                return 0;
+
+        r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds);
+        if (r < 0)
+                return r;
+
+        /* Don't trust augmented credentials for authorization */
+        assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM);
+
+        r = sd_bus_creds_get_euid(creds, &sender_uid);
+        if (r < 0)
+                return r;
+
+        return sender_uid == good_user;
+}
+
+#if ENABLE_POLKIT
+static int bus_message_append_strv_key_value(
+                sd_bus_message *m,
+                const char **l) {
+
+        const char **k, **v;
+        int r;
+
+        assert(m);
+
+        r = sd_bus_message_open_container(m, 'a', "{ss}");
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH_PAIR(k, v, l) {
+                r = sd_bus_message_append(m, "{ss}", *k, *v);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_bus_message_close_container(m);
+        if (r < 0)
+                return r;
+
+        return r;
+}
+#endif
+
+int bus_test_polkit(
+                sd_bus_message *call,
+                int capability,
+                const char *action,
+                const char **details,
+                uid_t good_user,
+                bool *_challenge,
+                sd_bus_error *ret_error) {
+
+        int r;
+
+        assert(call);
+        assert(action);
+
+        /* Tests non-interactively! */
+
+        r = check_good_user(call, good_user);
+        if (r != 0)
+                return r;
+
+        r = sd_bus_query_sender_privilege(call, capability);
+        if (r < 0)
+                return r;
+        else if (r > 0)
+                return 1;
+#if ENABLE_POLKIT
+        else {
+                _cleanup_(sd_bus_message_unrefp) sd_bus_message *request = NULL;
+                _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+                int authorized = false, challenge = false;
+                const char *sender;
+
+                sender = sd_bus_message_get_sender(call);
+                if (!sender)
+                        return -EBADMSG;
+
+                r = sd_bus_message_new_method_call(
+                                call->bus,
+                                &request,
+                                "org.freedesktop.PolicyKit1",
+                                "/org/freedesktop/PolicyKit1/Authority",
+                                "org.freedesktop.PolicyKit1.Authority",
+                                "CheckAuthorization");
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_append(
+                                request,
+                                "(sa{sv})s",
+                                "system-bus-name", 1, "name", "s", sender,
+                                action);
+                if (r < 0)
+                        return r;
+
+                r = bus_message_append_strv_key_value(request, details);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_append(request, "us", 0, NULL);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_call(call->bus, request, 0, ret_error, &reply);
+                if (r < 0) {
+                        /* Treat no PK available as access denied */
+                        if (sd_bus_error_has_name(ret_error, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
+                                sd_bus_error_free(ret_error);
+                                return -EACCES;
+                        }
+
+                        return r;
+                }
+
+                r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
+                if (r < 0)
+                        return r;
+
+                if (authorized)
+                        return 1;
+
+                if (_challenge) {
+                        *_challenge = challenge;
+                        return 0;
+                }
+        }
+#endif
+
+        return -EACCES;
+}
+
+#if ENABLE_POLKIT
+
+typedef struct AsyncPolkitQuery {
+        char *action;
+        char **details;
+
+        sd_bus_message *request, *reply;
+        sd_bus_slot *slot;
+
+        Hashmap *registry;
+        sd_event_source *defer_event_source;
+} AsyncPolkitQuery;
+
+static void async_polkit_query_free(AsyncPolkitQuery *q) {
+        if (!q)
+                return;
+
+        sd_bus_slot_unref(q->slot);
+
+        if (q->registry && q->request)
+                hashmap_remove(q->registry, q->request);
+
+        sd_bus_message_unref(q->request);
+        sd_bus_message_unref(q->reply);
+
+        free(q->action);
+        strv_free(q->details);
+
+        sd_event_source_disable_unref(q->defer_event_source);
+        free(q);
+}
+
+static int async_polkit_defer(sd_event_source *s, void *userdata) {
+        AsyncPolkitQuery *q = userdata;
+
+        assert(s);
+
+        /* This is called as idle event source after we processed the async polkit reply, hopefully after the
+         * method call we re-enqueued has been properly processed. */
+
+        async_polkit_query_free(q);
+        return 0;
+}
+
+static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
+        AsyncPolkitQuery *q = userdata;
+        int r;
+
+        assert(reply);
+        assert(q);
+
+        assert(q->slot);
+        q->slot = sd_bus_slot_unref(q->slot);
+
+        assert(!q->reply);
+        q->reply = sd_bus_message_ref(reply);
+
+        /* Now, let's dispatch the original message a second time be re-enqueing. This will then traverse the
+         * whole message processing again, and thus re-validating and re-retrieving the "userdata" field
+         * again.
+         *
+         * We install an idle event loop event to clean-up the PolicyKit request data when we are idle again,
+         * i.e. after the second time the message is processed is complete. */
+
+        assert(!q->defer_event_source);
+        r = sd_event_add_defer(sd_bus_get_event(sd_bus_message_get_bus(reply)), &q->defer_event_source, async_polkit_defer, q);
+        if (r < 0)
+                goto fail;
+
+        r = sd_event_source_set_priority(q->defer_event_source, SD_EVENT_PRIORITY_IDLE);
+        if (r < 0)
+                goto fail;
+
+        r = sd_event_source_set_enabled(q->defer_event_source, SD_EVENT_ONESHOT);
+        if (r < 0)
+                goto fail;
+
+        r = sd_bus_message_rewind(q->request, true);
+        if (r < 0)
+                goto fail;
+
+        r = sd_bus_enqueue_for_read(sd_bus_message_get_bus(q->request), q->request);
+        if (r < 0)
+                goto fail;
+
+        return 1;
+
+fail:
+        log_debug_errno(r, "Processing asynchronous PolicyKit reply failed, ignoring: %m");
+        (void) sd_bus_reply_method_errno(q->request, r, NULL);
+        async_polkit_query_free(q);
+        return r;
+}
+
+#endif
+
+int bus_verify_polkit_async(
+                sd_bus_message *call,
+                int capability,
+                const char *action,
+                const char **details,
+                bool interactive,
+                uid_t good_user,
+                Hashmap **registry,
+                sd_bus_error *ret_error) {
+
+#if ENABLE_POLKIT
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL;
+        AsyncPolkitQuery *q;
+        int c;
+#endif
+        const char *sender;
+        int r;
+
+        assert(call);
+        assert(action);
+        assert(registry);
+
+        r = check_good_user(call, good_user);
+        if (r != 0)
+                return r;
+
+#if ENABLE_POLKIT
+        q = hashmap_get(*registry, call);
+        if (q) {
+                int authorized, challenge;
+
+                /* This is the second invocation of this function, and there's already a response from
+                 * polkit, let's process it */
+                assert(q->reply);
+
+                /* If the operation we want to authenticate changed between the first and the second time,
+                 * let's not use this authentication, it might be out of date as the object and context we
+                 * operate on might have changed. */
+                if (!streq(q->action, action) ||
+                    !strv_equal(q->details, (char**) details))
+                        return -ESTALE;
+
+                if (sd_bus_message_is_method_error(q->reply, NULL)) {
+                        const sd_bus_error *e;
+
+                        e = sd_bus_message_get_error(q->reply);
+
+                        /* Treat no PK available as access denied */
+                        if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN) ||
+                            sd_bus_error_has_name(e, SD_BUS_ERROR_NAME_HAS_NO_OWNER))
+                                return -EACCES;
+
+                        /* Copy error from polkit reply */
+                        sd_bus_error_copy(ret_error, e);
+                        return -sd_bus_error_get_errno(e);
+                }
+
+                r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
+                if (r >= 0)
+                        r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
+                if (r < 0)
+                        return r;
+
+                if (authorized)
+                        return 1;
+
+                if (challenge)
+                        return sd_bus_error_set(ret_error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, "Interactive authentication required.");
+
+                return -EACCES;
+        }
+#endif
+
+        r = sd_bus_query_sender_privilege(call, capability);
+        if (r < 0)
+                return r;
+        else if (r > 0)
+                return 1;
+
+        sender = sd_bus_message_get_sender(call);
+        if (!sender)
+                return -EBADMSG;
+
+#if ENABLE_POLKIT
+        c = sd_bus_message_get_allow_interactive_authorization(call);
+        if (c < 0)
+                return c;
+        if (c > 0)
+                interactive = true;
+
+        r = hashmap_ensure_allocated(registry, NULL);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_new_method_call(
+                        call->bus,
+                        &pk,
+                        "org.freedesktop.PolicyKit1",
+                        "/org/freedesktop/PolicyKit1/Authority",
+                        "org.freedesktop.PolicyKit1.Authority",
+                        "CheckAuthorization");
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(
+                        pk,
+                        "(sa{sv})s",
+                        "system-bus-name", 1, "name", "s", sender,
+                        action);
+        if (r < 0)
+                return r;
+
+        r = bus_message_append_strv_key_value(pk, details);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(pk, "us", interactive, NULL);
+        if (r < 0)
+                return r;
+
+        q = new(AsyncPolkitQuery, 1);
+        if (!q)
+                return -ENOMEM;
+
+        *q = (AsyncPolkitQuery) {
+                .request = sd_bus_message_ref(call),
+        };
+
+        q->action = strdup(action);
+        if (!q->action) {
+                async_polkit_query_free(q);
+                return -ENOMEM;
+        }
+
+        q->details = strv_copy((char**) details);
+        if (!q->details) {
+                async_polkit_query_free(q);
+                return -ENOMEM;
+        }
+
+        r = hashmap_put(*registry, call, q);
+        if (r < 0) {
+                async_polkit_query_free(q);
+                return r;
+        }
+
+        q->registry = *registry;
+
+        r = sd_bus_call_async(call->bus, &q->slot, pk, async_polkit_callback, q, 0);
+        if (r < 0) {
+                async_polkit_query_free(q);
+                return r;
+        }
+
+        return 0;
+#endif
+
+        return -EACCES;
+}
+
+void bus_verify_polkit_async_registry_free(Hashmap *registry) {
+#if ENABLE_POLKIT
+        hashmap_free_with_destructor(registry, async_polkit_query_free);
+#endif
+}
diff --git a/src/shared/bus-polkit.h b/src/shared/bus-polkit.h
new file mode 100644 (file)
index 0000000..29b3923
--- /dev/null
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+#include "hashmap.h"
+
+int bus_test_polkit(sd_bus_message *call, int capability, const char *action, const char **details, uid_t good_user, bool *_challenge, sd_bus_error *e);
+
+int bus_verify_polkit_async(sd_bus_message *call, int capability, const char *action, const char **details, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error);
+void bus_verify_polkit_async_registry_free(Hashmap *registry);
index 22a15493d7f3b8e6568437f205a45480e4a7bb95..28d85944a8a73b0b3500d98ed90a7b135de13204 100644 (file)
@@ -833,7 +833,8 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                               "RuntimeDirectoryPreserve",
                               "Personality",
                               "KeyringMode",
-                              "NetworkNamespacePath"))
+                              "NetworkNamespacePath",
+                              "LogNamespace"))
                 return bus_append_string(m, field, eq);
 
         if (STR_IN_SET(field, "IgnoreSIGPIPE",
@@ -854,6 +855,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                               "ProtectKernelTunables",
                               "ProtectKernelModules",
                               "ProtectKernelLogs",
+                              "ProtectClock",
                               "ProtectControlGroups",
                               "MountAPIVFS",
                               "CPUSchedulingResetOnFork",
index 10c05eba184772a94255bd8d259ac98bec59f989..8e6a6e2ce2dedfb1f1340f497bc916d486353253 100644 (file)
@@ -9,7 +9,6 @@
 #include <sys/socket.h>
 #include <unistd.h>
 
-#include "sd-bus-protocol.h"
 #include "sd-bus.h"
 #include "sd-daemon.h"
 #include "sd-event.h"
 #include "bus-util.h"
 #include "cap-list.h"
 #include "cgroup-util.h"
-#include "def.h"
-#include "escape.h"
-#include "fd-util.h"
 #include "mountpoint-util.h"
 #include "nsflags.h"
 #include "parse-util.h"
 #include "path-util.h"
-#include "proc-cmdline.h"
 #include "rlimit-util.h"
+#include "socket-util.h"
 #include "stdio-util.h"
 #include "strv.h"
 #include "user-util.h"
@@ -185,357 +181,6 @@ int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) {
         return has_owner;
 }
 
-static int check_good_user(sd_bus_message *m, uid_t good_user) {
-        _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
-        uid_t sender_uid;
-        int r;
-
-        assert(m);
-
-        if (good_user == UID_INVALID)
-                return 0;
-
-        r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds);
-        if (r < 0)
-                return r;
-
-        /* Don't trust augmented credentials for authorization */
-        assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM);
-
-        r = sd_bus_creds_get_euid(creds, &sender_uid);
-        if (r < 0)
-                return r;
-
-        return sender_uid == good_user;
-}
-
-int bus_test_polkit(
-                sd_bus_message *call,
-                int capability,
-                const char *action,
-                const char **details,
-                uid_t good_user,
-                bool *_challenge,
-                sd_bus_error *e) {
-
-        int r;
-
-        assert(call);
-        assert(action);
-
-        /* Tests non-interactively! */
-
-        r = check_good_user(call, good_user);
-        if (r != 0)
-                return r;
-
-        r = sd_bus_query_sender_privilege(call, capability);
-        if (r < 0)
-                return r;
-        else if (r > 0)
-                return 1;
-#if ENABLE_POLKIT
-        else {
-                _cleanup_(sd_bus_message_unrefp) sd_bus_message *request = NULL;
-                _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
-                int authorized = false, challenge = false;
-                const char *sender, **k, **v;
-
-                sender = sd_bus_message_get_sender(call);
-                if (!sender)
-                        return -EBADMSG;
-
-                r = sd_bus_message_new_method_call(
-                                call->bus,
-                                &request,
-                                "org.freedesktop.PolicyKit1",
-                                "/org/freedesktop/PolicyKit1/Authority",
-                                "org.freedesktop.PolicyKit1.Authority",
-                                "CheckAuthorization");
-                if (r < 0)
-                        return r;
-
-                r = sd_bus_message_append(
-                                request,
-                                "(sa{sv})s",
-                                "system-bus-name", 1, "name", "s", sender,
-                                action);
-                if (r < 0)
-                        return r;
-
-                r = sd_bus_message_open_container(request, 'a', "{ss}");
-                if (r < 0)
-                        return r;
-
-                STRV_FOREACH_PAIR(k, v, details) {
-                        r = sd_bus_message_append(request, "{ss}", *k, *v);
-                        if (r < 0)
-                                return r;
-                }
-
-                r = sd_bus_message_close_container(request);
-                if (r < 0)
-                        return r;
-
-                r = sd_bus_message_append(request, "us", 0, NULL);
-                if (r < 0)
-                        return r;
-
-                r = sd_bus_call(call->bus, request, 0, e, &reply);
-                if (r < 0) {
-                        /* Treat no PK available as access denied */
-                        if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
-                                sd_bus_error_free(e);
-                                return -EACCES;
-                        }
-
-                        return r;
-                }
-
-                r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
-                if (r < 0)
-                        return r;
-
-                r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
-                if (r < 0)
-                        return r;
-
-                if (authorized)
-                        return 1;
-
-                if (_challenge) {
-                        *_challenge = challenge;
-                        return 0;
-                }
-        }
-#endif
-
-        return -EACCES;
-}
-
-#if ENABLE_POLKIT
-
-typedef struct AsyncPolkitQuery {
-        sd_bus_message *request, *reply;
-        sd_bus_message_handler_t callback;
-        void *userdata;
-        sd_bus_slot *slot;
-        Hashmap *registry;
-} AsyncPolkitQuery;
-
-static void async_polkit_query_free(AsyncPolkitQuery *q) {
-
-        if (!q)
-                return;
-
-        sd_bus_slot_unref(q->slot);
-
-        if (q->registry && q->request)
-                hashmap_remove(q->registry, q->request);
-
-        sd_bus_message_unref(q->request);
-        sd_bus_message_unref(q->reply);
-
-        free(q);
-}
-
-static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
-        _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
-        AsyncPolkitQuery *q = userdata;
-        int r;
-
-        assert(reply);
-        assert(q);
-
-        q->slot = sd_bus_slot_unref(q->slot);
-        q->reply = sd_bus_message_ref(reply);
-
-        r = sd_bus_message_rewind(q->request, true);
-        if (r < 0) {
-                r = sd_bus_reply_method_errno(q->request, r, NULL);
-                goto finish;
-        }
-
-        r = q->callback(q->request, q->userdata, &error_buffer);
-        r = bus_maybe_reply_error(q->request, r, &error_buffer);
-
-finish:
-        async_polkit_query_free(q);
-
-        return r;
-}
-
-#endif
-
-int bus_verify_polkit_async(
-                sd_bus_message *call,
-                int capability,
-                const char *action,
-                const char **details,
-                bool interactive,
-                uid_t good_user,
-                Hashmap **registry,
-                sd_bus_error *error) {
-
-#if ENABLE_POLKIT
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL;
-        AsyncPolkitQuery *q;
-        const char *sender, **k, **v;
-        sd_bus_message_handler_t callback;
-        void *userdata;
-        int c;
-#endif
-        int r;
-
-        assert(call);
-        assert(action);
-        assert(registry);
-
-        r = check_good_user(call, good_user);
-        if (r != 0)
-                return r;
-
-#if ENABLE_POLKIT
-        q = hashmap_get(*registry, call);
-        if (q) {
-                int authorized, challenge;
-
-                /* This is the second invocation of this function, and
-                 * there's already a response from polkit, let's
-                 * process it */
-                assert(q->reply);
-
-                if (sd_bus_message_is_method_error(q->reply, NULL)) {
-                        const sd_bus_error *e;
-
-                        e = sd_bus_message_get_error(q->reply);
-
-                        /* Treat no PK available as access denied */
-                        if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN) ||
-                            sd_bus_error_has_name(e, SD_BUS_ERROR_NAME_HAS_NO_OWNER))
-                                return -EACCES;
-
-                        /* Copy error from polkit reply */
-                        sd_bus_error_copy(error, e);
-                        return -sd_bus_error_get_errno(e);
-                }
-
-                r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
-                if (r >= 0)
-                        r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
-                if (r < 0)
-                        return r;
-
-                if (authorized)
-                        return 1;
-
-                if (challenge)
-                        return sd_bus_error_set(error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, "Interactive authentication required.");
-
-                return -EACCES;
-        }
-#endif
-
-        r = sd_bus_query_sender_privilege(call, capability);
-        if (r < 0)
-                return r;
-        else if (r > 0)
-                return 1;
-
-#if ENABLE_POLKIT
-        if (sd_bus_get_current_message(call->bus) != call)
-                return -EINVAL;
-
-        callback = sd_bus_get_current_handler(call->bus);
-        if (!callback)
-                return -EINVAL;
-
-        userdata = sd_bus_get_current_userdata(call->bus);
-
-        sender = sd_bus_message_get_sender(call);
-        if (!sender)
-                return -EBADMSG;
-
-        c = sd_bus_message_get_allow_interactive_authorization(call);
-        if (c < 0)
-                return c;
-        if (c > 0)
-                interactive = true;
-
-        r = hashmap_ensure_allocated(registry, NULL);
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_new_method_call(
-                        call->bus,
-                        &pk,
-                        "org.freedesktop.PolicyKit1",
-                        "/org/freedesktop/PolicyKit1/Authority",
-                        "org.freedesktop.PolicyKit1.Authority",
-                        "CheckAuthorization");
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_append(
-                        pk,
-                        "(sa{sv})s",
-                        "system-bus-name", 1, "name", "s", sender,
-                        action);
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_open_container(pk, 'a', "{ss}");
-        if (r < 0)
-                return r;
-
-        STRV_FOREACH_PAIR(k, v, details) {
-                r = sd_bus_message_append(pk, "{ss}", *k, *v);
-                if (r < 0)
-                        return r;
-        }
-
-        r = sd_bus_message_close_container(pk);
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_append(pk, "us", interactive, NULL);
-        if (r < 0)
-                return r;
-
-        q = new0(AsyncPolkitQuery, 1);
-        if (!q)
-                return -ENOMEM;
-
-        q->request = sd_bus_message_ref(call);
-        q->callback = callback;
-        q->userdata = userdata;
-
-        r = hashmap_put(*registry, call, q);
-        if (r < 0) {
-                async_polkit_query_free(q);
-                return r;
-        }
-
-        q->registry = *registry;
-
-        r = sd_bus_call_async(call->bus, &q->slot, pk, async_polkit_callback, q, 0);
-        if (r < 0) {
-                async_polkit_query_free(q);
-                return r;
-        }
-
-        return 0;
-#endif
-
-        return -EACCES;
-}
-
-void bus_verify_polkit_async_registry_free(Hashmap *registry) {
-#if ENABLE_POLKIT
-        hashmap_free_with_destructor(registry, async_polkit_query_free);
-#endif
-}
-
 int bus_check_peercred(sd_bus *c) {
         struct ucred ucred;
         int fd, r;
@@ -761,7 +406,7 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b
                                 if (r < 0)
                                         return r;
 
-                                result = s;
+                                result = strempty(s);
                         }
 
                         bus_print_property_value(name, expected_value, value, result);
index 1e2f04cc5dffcfc21fdb33bbb9eb8e56d828bc7e..db245a791ea4fa4c21450e0394f41993e5f38dde 100644 (file)
@@ -9,8 +9,8 @@
 #include "sd-bus.h"
 #include "sd-event.h"
 
-#include "hashmap.h"
 #include "macro.h"
+#include "set.h"
 #include "string-util.h"
 #include "time-util.h"
 
@@ -52,11 +52,6 @@ int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error);
 
 int bus_check_peercred(sd_bus *c);
 
-int bus_test_polkit(sd_bus_message *call, int capability, const char *action, const char **details, uid_t good_user, bool *_challenge, sd_bus_error *e);
-
-int bus_verify_polkit_async(sd_bus_message *call, int capability, const char *action, const char **details, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error);
-void bus_verify_polkit_async_registry_free(Hashmap *registry);
-
 int bus_connect_system_systemd(sd_bus **_bus);
 int bus_connect_user_systemd(sd_bus **_bus);
 
index cb20279dda602ad45276dc223fdc1761b0d622c9..657df0a517a1bb96a9547f7f21c3ae1df9a2ecd6 100644 (file)
@@ -334,6 +334,8 @@ int config_parse(const char *unit,
                         return r;
                 }
 
+                line++;
+
                 l = skip_leading_chars(buf, WHITESPACE);
                 if (*l != '\0' && strchr(COMMENTS, *l))
                         continue;
@@ -390,7 +392,7 @@ int config_parse(const char *unit,
 
                 r = parse_line(unit,
                                filename,
-                               ++line,
+                               line,
                                sections,
                                lookup,
                                table,
@@ -515,6 +517,7 @@ DEFINE_PARSER(long, long, safe_atoli);
 DEFINE_PARSER(uint8, uint8_t, safe_atou8);
 DEFINE_PARSER(uint16, uint16_t, safe_atou16);
 DEFINE_PARSER(uint32, uint32_t, safe_atou32);
+DEFINE_PARSER(int32, int32_t, safe_atoi32);
 DEFINE_PARSER(uint64, uint64_t, safe_atou64);
 DEFINE_PARSER(unsigned, unsigned, safe_atou);
 DEFINE_PARSER(double, double, safe_atod);
@@ -555,7 +558,7 @@ int config_parse_iec_size(const char* unit,
         return 0;
 }
 
-int config_parse_si_size(
+int config_parse_si_uint64(
                 const char* unit,
                 const char *filename,
                 unsigned line,
@@ -567,8 +570,7 @@ int config_parse_si_size(
                 void *data,
                 void *userdata) {
 
-        size_t *sz = data;
-        uint64_t v;
+        uint64_t *sz = data;
         int r;
 
         assert(filename);
@@ -576,15 +578,12 @@ int config_parse_si_size(
         assert(rvalue);
         assert(data);
 
-        r = parse_size(rvalue, 1000, &v);
-        if (r >= 0 && (uint64_t) (size_t) v != v)
-                r = -ERANGE;
+        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;
         }
 
-        *sz = (size_t) v;
         return 0;
 }
 
index 287620ad71431a7854e3fa81c5c61f974bb981b6..2f3cb4217d01ebda574db82f7de8cdf3450c3933 100644 (file)
@@ -115,10 +115,11 @@ CONFIG_PARSER_PROTOTYPE(config_parse_long);
 CONFIG_PARSER_PROTOTYPE(config_parse_uint8);
 CONFIG_PARSER_PROTOTYPE(config_parse_uint16);
 CONFIG_PARSER_PROTOTYPE(config_parse_uint32);
+CONFIG_PARSER_PROTOTYPE(config_parse_int32);
 CONFIG_PARSER_PROTOTYPE(config_parse_uint64);
 CONFIG_PARSER_PROTOTYPE(config_parse_double);
 CONFIG_PARSER_PROTOTYPE(config_parse_iec_size);
-CONFIG_PARSER_PROTOTYPE(config_parse_si_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_tristate);
index 11d21c3a4d1b900af3883edf2e28034590a5521e..1ef69fdf4c8d3bc47a1563bb609a227c9fd4af3d 100644 (file)
@@ -28,6 +28,7 @@
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
+#include "fsck-util.h"
 #include "gpt.h"
 #include "hexdecoct.h"
 #include "hostname-util.h"
@@ -278,6 +279,29 @@ static int loop_wait_for_partitions_to_appear(
                                N_DEVICE_NODE_LIST_ATTEMPTS);
 }
 
+static void check_partition_flags(
+                const char *node,
+                unsigned long long pflags,
+                unsigned long long supported) {
+
+        assert(node);
+
+        /* Mask away all flags supported by this partition's type and the three flags the UEFI spec defines generically */
+        pflags &= ~(supported | GPT_FLAG_REQUIRED_PARTITION | GPT_FLAG_NO_BLOCK_IO_PROTOCOL | GPT_FLAG_LEGACY_BIOS_BOOTABLE);
+
+        if (pflags == 0)
+                return;
+
+        /* If there are other bits set, then log about it, to make things discoverable */
+        for (unsigned i = 0; i < sizeof(pflags) * 8; i++) {
+                unsigned long long bit = 1ULL << i;
+                if (!FLAGS_SET(pflags, bit))
+                        continue;
+
+                log_debug("Unexpected partition flag %llu set on %s!", bit, node);
+        }
+}
+
 #endif
 
 int dissect_image(
@@ -401,10 +425,11 @@ int dissect_image(
 
                         m->encrypted = streq_ptr(fstype, "crypto_LUKS");
 
-                        r = loop_wait_for_partitions_to_appear(fd, d, 0, flags, &e);
-                        if (r < 0)
-                                return r;
-
+                        if (!streq(usage, "filesystem")) {
+                                r = loop_wait_for_partitions_to_appear(fd, d, 0, flags, &e);
+                                if (r < 0)
+                                        return r;
+                        }
                         *ret = TAKE_PTR(m);
 
                         return 0;
@@ -484,6 +509,8 @@ int dissect_image(
 
                         if (sd_id128_equal(type_id, GPT_HOME)) {
 
+                                check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
                                 if (pflags & GPT_FLAG_NO_AUTO)
                                         continue;
 
@@ -491,6 +518,8 @@ int dissect_image(
                                 rw = !(pflags & GPT_FLAG_READ_ONLY);
                         } else if (sd_id128_equal(type_id, GPT_SRV)) {
 
+                                check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
                                 if (pflags & GPT_FLAG_NO_AUTO)
                                         continue;
 
@@ -510,6 +539,8 @@ int dissect_image(
 
                         } else if (sd_id128_equal(type_id, GPT_XBOOTLDR)) {
 
+                                check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
                                 if (pflags & GPT_FLAG_NO_AUTO)
                                         continue;
 
@@ -519,6 +550,8 @@ int dissect_image(
 #ifdef GPT_ROOT_NATIVE
                         else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) {
 
+                                check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
                                 if (pflags & GPT_FLAG_NO_AUTO)
                                         continue;
 
@@ -531,6 +564,8 @@ int dissect_image(
                                 rw = !(pflags & GPT_FLAG_READ_ONLY);
                         } else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY)) {
 
+                                check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
                                 if (pflags & GPT_FLAG_NO_AUTO)
                                         continue;
 
@@ -549,6 +584,8 @@ int dissect_image(
 #ifdef GPT_ROOT_SECONDARY
                         else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) {
 
+                                check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
                                 if (pflags & GPT_FLAG_NO_AUTO)
                                         continue;
 
@@ -561,6 +598,8 @@ int dissect_image(
                                 rw = !(pflags & GPT_FLAG_READ_ONLY);
                         } else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY)) {
 
+                                check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
                                 if (pflags & GPT_FLAG_NO_AUTO)
                                         continue;
 
@@ -578,6 +617,8 @@ int dissect_image(
 #endif
                         else if (sd_id128_equal(type_id, GPT_SWAP)) {
 
+                                check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO);
+
                                 if (pflags & GPT_FLAG_NO_AUTO)
                                         continue;
 
@@ -585,6 +626,8 @@ int dissect_image(
                                 fstype = "swap";
                         } else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) {
 
+                                check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
                                 if (pflags & GPT_FLAG_NO_AUTO)
                                         continue;
 
@@ -598,6 +641,47 @@ int dissect_image(
                                         if (!generic_node)
                                                 return -ENOMEM;
                                 }
+
+                        } else if (sd_id128_equal(type_id, GPT_TMP)) {
+
+                                check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
+                                if (pflags & GPT_FLAG_NO_AUTO)
+                                        continue;
+
+                                designator = PARTITION_TMP;
+                                rw = !(pflags & GPT_FLAG_READ_ONLY);
+
+                        } else if (sd_id128_equal(type_id, GPT_VAR)) {
+
+                                check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
+
+                                if (pflags & GPT_FLAG_NO_AUTO)
+                                        continue;
+
+                                if (!FLAGS_SET(flags, DISSECT_IMAGE_RELAX_VAR_CHECK)) {
+                                        sd_id128_t var_uuid;
+
+                                        /* For /var we insist that the uuid of the partition matches the
+                                         * HMAC-SHA256 of the /var GPT partition type uuid, keyed by machine
+                                         * ID. Why? Unlike the other partitions /var is inherently
+                                         * installation specific, hence we need to be careful not to mount it
+                                         * in the wrong installation. By hashing the partition UUID from
+                                         * /etc/machine-id we can securely bind the partition to the
+                                         * installation. */
+
+                                        r = sd_id128_get_machine_app_specific(GPT_VAR, &var_uuid);
+                                        if (r < 0)
+                                                return r;
+
+                                        if (!sd_id128_equal(var_uuid, id)) {
+                                                log_debug("Found a /var/ partition, but its UUID didn't match our expectations, ignoring.");
+                                                continue;
+                                        }
+                                }
+
+                                designator = PARTITION_VAR;
+                                rw = !(pflags & GPT_FLAG_READ_ONLY);
                         }
 
                         if (designator != _PARTITION_DESIGNATOR_INVALID) {
@@ -814,6 +898,49 @@ static int is_loop_device(const char *path) {
         return true;
 }
 
+static int run_fsck(const char *node, const char *fstype) {
+        int r, exit_status;
+        pid_t pid;
+
+        assert(node);
+        assert(fstype);
+
+        r = fsck_exists(fstype);
+        if (r < 0) {
+                log_debug_errno(r, "Couldn't determine whether fsck for %s exists, proceeding anyway.", fstype);
+                return 0;
+        }
+        if (r == 0) {
+                log_debug("Not checking partition %s, as fsck for %s does not exist.", node, fstype);
+                return 0;
+        }
+
+        r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_NULL_STDIO, &pid);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to fork off fsck: %m");
+        if (r == 0) {
+                /* Child */
+                execl("/sbin/fsck", "/sbin/fsck", "-aT", node, NULL);
+                log_debug_errno(errno, "Failed to execl() fsck: %m");
+                _exit(FSCK_OPERATIONAL_ERROR);
+        }
+
+        exit_status = wait_for_terminate_and_check("fsck", pid, 0);
+        if (exit_status < 0)
+                return log_debug_errno(exit_status, "Failed to fork off /sbin/fsck: %m");
+
+        if ((exit_status & ~FSCK_ERROR_CORRECTED) != FSCK_SUCCESS) {
+                log_debug("fsck failed with exit status %i.", exit_status);
+
+                if ((exit_status & (FSCK_SYSTEM_SHOULD_REBOOT|FSCK_ERRORS_LEFT_UNCORRECTED)) != 0)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN), "File system is corrupted, refusing.");
+
+                log_debug("Ignoring fsck error.");
+        }
+
+        return 0;
+}
+
 static int mount_partition(
                 DissectedPartition *m,
                 const char *where,
@@ -841,6 +968,12 @@ static int mount_partition(
 
         rw = m->rw && !(flags & DISSECT_IMAGE_READ_ONLY);
 
+        if (FLAGS_SET(flags, DISSECT_IMAGE_FSCK) && rw) {
+                r = run_fsck(node, fstype);
+                if (r < 0)
+                        return r;
+        }
+
         if (directory) {
                 r = chase_symlinks(directory, where, CHASE_PREFIX_ROOT, &chased, NULL);
                 if (r < 0)
@@ -910,6 +1043,14 @@ int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift,
         if (r < 0)
                 return r;
 
+        r = mount_partition(m->partitions + PARTITION_VAR, where, "/var", uid_shift, flags);
+        if (r < 0)
+                return r;
+
+        r = mount_partition(m->partitions + PARTITION_TMP, where, "/var/tmp", uid_shift, flags);
+        if (r < 0)
+                return r;
+
         boot_mounted = mount_partition(m->partitions + PARTITION_XBOOTLDR, where, "/boot", uid_shift, flags);
         if (boot_mounted < 0)
                 return boot_mounted;
@@ -1333,7 +1474,8 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
                 [META_HOSTNAME]     = "/etc/hostname\0",
                 [META_MACHINE_ID]   = "/etc/machine-id\0",
                 [META_MACHINE_INFO] = "/etc/machine-info\0",
-                [META_OS_RELEASE]   = "/etc/os-release\0/usr/lib/os-release\0",
+                [META_OS_RELEASE]   = "/etc/os-release\0"
+                                      "/usr/lib/os-release\0",
         };
 
         _cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL;
@@ -1528,6 +1670,8 @@ static const char *const partition_designator_table[] = {
         [PARTITION_SWAP] = "swap",
         [PARTITION_ROOT_VERITY] = "root-verity",
         [PARTITION_ROOT_SECONDARY_VERITY] = "root-secondary-verity",
+        [PARTITION_TMP] = "tmp",
+        [PARTITION_VAR] = "var",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(partition_designator, int);
index 40b8035ac7f6d870ba843c651eefb7dfa086235c..6a666ca7c70bec70eaf39666b3705fdc10816040 100644 (file)
@@ -33,6 +33,8 @@ enum  {
         PARTITION_SWAP,
         PARTITION_ROOT_VERITY, /* verity data for the PARTITION_ROOT partition */
         PARTITION_ROOT_SECONDARY_VERITY, /* verity data for the PARTITION_ROOT_SECONDARY partition */
+        PARTITION_TMP,
+        PARTITION_VAR,
         _PARTITION_DESIGNATOR_MAX,
         _PARTITION_DESIGNATOR_INVALID = -1
 };
@@ -59,6 +61,8 @@ typedef enum DissectImageFlags {
         DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY = 1 << 7,  /* Mount only non-root partitions */
         DISSECT_IMAGE_VALIDATE_OS         = 1 << 8,  /* Refuse mounting images that aren't identifiable as OS images */
         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) */
 } DissectImageFlags;
 
 struct DissectedImage {
index 108f31d502bb76b41409f3ceb5f2f69aa052ea85..b05dc91ecf71bffb5ef7262eee67f6c12ac6f685 100644 (file)
@@ -63,40 +63,6 @@ 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));
 
-bool is_efi_boot(void) {
-        if (detect_container() > 0)
-                return false;
-
-        return access("/sys/firmware/efi/", F_OK) >= 0;
-}
-
-static int read_flag(const char *varname) {
-        _cleanup_free_ void *v = NULL;
-        uint8_t b;
-        size_t s;
-        int r;
-
-        if (!is_efi_boot()) /* If this is not an EFI boot, assume the queried flags are zero */
-                return 0;
-
-        r = efi_get_variable(EFI_VENDOR_GLOBAL, varname, NULL, &v, &s);
-        if (r < 0)
-                return r;
-
-        if (s != 1)
-                return -EINVAL;
-
-        b = *(uint8_t *)v;
-        return !!b;
-}
-
-bool is_efi_secure_boot(void) {
-        return read_flag("SecureBoot") > 0;
-}
-
-bool is_efi_secure_boot_setup_mode(void) {
-        return read_flag("SetupMode") > 0;
-}
 
 int efi_reboot_to_firmware_supported(void) {
         _cleanup_free_ void *v = NULL;
index 7d41fbb3593608b2128d0d62171346e689654f97..96208d25bf5ea053687d156e579063cf72164d64 100644 (file)
@@ -5,9 +5,6 @@
 
 #if ENABLE_EFI
 
-bool is_efi_boot(void);
-bool is_efi_secure_boot(void);
-bool is_efi_secure_boot_setup_mode(void);
 int efi_reboot_to_firmware_supported(void);
 int efi_get_reboot_to_firmware(void);
 int efi_set_reboot_to_firmware(bool value);
@@ -28,18 +25,6 @@ int efi_loader_get_features(uint64_t *ret);
 
 #else
 
-static inline bool is_efi_boot(void) {
-        return false;
-}
-
-static inline bool is_efi_secure_boot(void) {
-        return false;
-}
-
-static inline bool is_efi_secure_boot_setup_mode(void) {
-        return false;
-}
-
 static inline int efi_reboot_to_firmware_supported(void) {
         return -EOPNOTSUPP;
 }
index a9059a3ab7a0bd60245f7b06679ed40762defd10..fe29af24d079fa675a804d115a27a97d1e2e2643 100644 (file)
@@ -50,6 +50,8 @@ DEFINE_STRING_TABLE_LOOKUP(port, NetDevPort);
 DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort, "Failed to parse Port setting");
 
 static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = {
+        [NET_DEV_FEAT_RX]   = "rx-checksum",
+        [NET_DEV_FEAT_TX]   = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
         [NET_DEV_FEAT_GSO]  = "tx-generic-segmentation",
         [NET_DEV_FEAT_GRO]  = "rx-gro",
         [NET_DEV_FEAT_LRO]  = "rx-lro",
@@ -177,7 +179,7 @@ int ethtool_get_driver(int *ethtool_fd, const char *ifname, char **ret) {
 }
 
 int ethtool_get_link_info(int *ethtool_fd, const char *ifname,
-                          int *ret_autonegotiation, size_t *ret_speed,
+                          int *ret_autonegotiation, uint64_t *ret_speed,
                           Duplex *ret_duplex, NetDevPort *ret_port) {
         struct ethtool_cmd ecmd = {
                 .cmd = ETHTOOL_GSET,
@@ -498,22 +500,38 @@ static int get_stringset(int ethtool_fd, struct ifreq *ifr, int stringset_id, st
         return 0;
 }
 
-static int find_feature_index(struct ethtool_gstrings *strings, const char *feature) {
-        unsigned i;
+static int set_features_bit(
+                const struct ethtool_gstrings *strings,
+                const char *feature,
+                bool flag,
+                struct ethtool_sfeatures *sfeatures) {
+        bool found = false;
 
-        for (i = 0; i < strings->len; i++) {
-                if (streq((char *) &strings->data[i * ETH_GSTRING_LEN], feature))
-                        return i;
-        }
+        assert(strings);
+        assert(feature);
+        assert(sfeatures);
+
+        for (size_t i = 0; i < strings->len; i++)
+                if (streq((char *) &strings->data[i * ETH_GSTRING_LEN], feature) ||
+                    (endswith(feature, "-") && startswith((char *) &strings->data[i * ETH_GSTRING_LEN], feature))) {
+                        size_t block, bit;
 
-        return -ENODATA;
+                        block = i / 32;
+                        bit = i % 32;
+
+                        sfeatures->features[block].valid |= 1 << bit;
+                        SET_FLAG(sfeatures->features[block].requested, 1 << bit, flag);
+                        found = true;
+                }
+
+        return found ? 0 : -ENODATA;
 }
 
 int ethtool_set_features(int *ethtool_fd, const char *ifname, int *features) {
         _cleanup_free_ struct ethtool_gstrings *strings = NULL;
         struct ethtool_sfeatures *sfeatures;
-        int block, bit, i, r;
         struct ifreq ifr = {};
+        int i, r;
 
         if (*ethtool_fd < 0) {
                 r = ethtool_connect_or_warn(ethtool_fd, true);
@@ -531,27 +549,14 @@ int ethtool_set_features(int *ethtool_fd, const char *ifname, int *features) {
         sfeatures->cmd = ETHTOOL_SFEATURES;
         sfeatures->size = DIV_ROUND_UP(strings->len, 32U);
 
-        for (i = 0; i < _NET_DEV_FEAT_MAX; i++) {
-
+        for (i = 0; i < _NET_DEV_FEAT_MAX; i++)
                 if (features[i] != -1) {
-
-                        r = find_feature_index(strings, netdev_feature_table[i]);
+                        r = set_features_bit(strings, netdev_feature_table[i], features[i], sfeatures);
                         if (r < 0) {
-                                log_warning_errno(r, "ethtool: could not find feature: %s", netdev_feature_table[i]);
+                                log_warning_errno(r, "ethtool: could not find feature, ignoring: %s", netdev_feature_table[i]);
                                 continue;
                         }
-
-                        block = r / 32;
-                        bit = r % 32;
-
-                        sfeatures->features[block].valid |= 1 << bit;
-
-                        if (features[i])
-                                sfeatures->features[block].requested |= 1 << bit;
-                        else
-                                sfeatures->features[block].requested &= ~(1 << bit);
                 }
-        }
 
         ifr.ifr_data = (void *) sfeatures;
 
@@ -734,7 +739,7 @@ int ethtool_set_glinksettings(
                 const char *ifname,
                 int autonegotiation,
                 uint32_t advertise[static N_ADVERTISE],
-                size_t speed,
+                uint64_t speed,
                 Duplex duplex,
                 NetDevPort port) {
         _cleanup_free_ struct ethtool_link_usettings *u = NULL;
@@ -846,6 +851,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,
index 8468a26bc2601937f3006ba6671408db9b221ff3..55c41f5bc751ef082e7ed32c1a50100947cf1139 100644 (file)
@@ -32,6 +32,8 @@ typedef enum WakeOnLan {
 } WakeOnLan;
 
 typedef enum NetDevFeature {
+        NET_DEV_FEAT_RX,
+        NET_DEV_FEAT_TX,
         NET_DEV_FEAT_GSO,
         NET_DEV_FEAT_GRO,
         NET_DEV_FEAT_LRO,
@@ -90,7 +92,7 @@ typedef struct netdev_ring_param {
 
 int ethtool_get_driver(int *ethtool_fd, const char *ifname, char **ret);
 int ethtool_get_link_info(int *ethtool_fd, const char *ifname,
-                          int *ret_autonegotiation, size_t *ret_speed,
+                          int *ret_autonegotiation, uint64_t *ret_speed,
                           Duplex *ret_duplex, NetDevPort *ret_port);
 int ethtool_get_permanent_macaddr(int *ethtool_fd, const char *ifname, struct ether_addr *ret);
 int ethtool_set_speed(int *ethtool_fd, const char *ifname, unsigned speed, Duplex duplex);
@@ -99,8 +101,9 @@ int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, netdev_ring
 int ethtool_set_features(int *ethtool_fd, const char *ifname, int *features);
 int ethtool_set_glinksettings(int *ethtool_fd, const char *ifname,
                               int autonegotiation, uint32_t advertise[static N_ADVERTISE],
-                              size_t speed, Duplex duplex, NetDevPort port);
+                              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 178bc78cc0f22f826cdaf885d43aa8df0b228d0a..4250130464910e521b95f09eb4415b420e8f2867 100644 (file)
@@ -4,12 +4,15 @@
 #include <net/if.h>
 #include <unistd.h>
 
+#include "sd-id128.h"
+
 #include "alloc-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "format-table.h"
 #include "format-util.h"
 #include "gunicode.h"
+#include "id128-util.h"
 #include "in-addr-util.h"
 #include "locale-util.h"
 #include "memory-util.h"
@@ -80,6 +83,7 @@ typedef struct TableData {
                 usec_t timespan;
                 uint64_t size;
                 char string[0];
+                char **strv;
                 int int_val;
                 int8_t int8;
                 int16_t int16;
@@ -93,6 +97,7 @@ typedef struct TableData {
                 int percent;        /* we use 'int' as datatype for percent values in order to match the result of parse_percent() */
                 int ifindex;
                 union in_addr_union address;
+                sd_id128_t id128;
                 /* … add more here as we start supporting more cell data types … */
         };
 } TableData;
@@ -207,6 +212,9 @@ static TableData *table_data_free(TableData *d) {
         free(d->formatted);
         free(d->url);
 
+        if (d->type == TABLE_STRV)
+                strv_free(d->strv);
+
         return mfree(d);
 }
 
@@ -242,6 +250,9 @@ static size_t table_data_size(TableDataType type, const void *data) {
         case TABLE_PATH:
                 return strlen(data) + 1;
 
+        case TABLE_STRV:
+                return sizeof(char **);
+
         case TABLE_BOOLEAN:
                 return sizeof(bool);
 
@@ -282,6 +293,10 @@ static size_t table_data_size(TableDataType type, const void *data) {
         case TABLE_IN6_ADDR:
                 return sizeof(struct in6_addr);
 
+        case TABLE_UUID:
+        case TABLE_ID128:
+                return sizeof(sd_id128_t);
+
         default:
                 assert_not_reached("Uh? Unexpected cell type");
         }
@@ -328,7 +343,6 @@ static bool table_data_matches(
 
         k = table_data_size(type, data);
         l = table_data_size(d->type, d->data);
-
         if (k != l)
                 return false;
 
@@ -344,8 +358,8 @@ static TableData *table_data_new(
                 unsigned align_percent,
                 unsigned ellipsize_percent) {
 
+        _cleanup_free_ TableData *d = NULL;
         size_t data_size;
-        TableData *d;
 
         data_size = table_data_size(type, data);
 
@@ -360,9 +374,15 @@ static TableData *table_data_new(
         d->weight = weight;
         d->align_percent = align_percent;
         d->ellipsize_percent = ellipsize_percent;
-        memcpy_safe(d->data, data, data_size);
 
-        return d;
+        if (type == TABLE_STRV) {
+                d->strv = strv_copy(data);
+                if (!d->strv)
+                        return NULL;
+        } else
+                memcpy_safe(d->data, data, data_size);
+
+        return TAKE_PTR(d);
 }
 
 int table_add_cell_full(
@@ -765,6 +785,7 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
                         int ifindex;
                         bool b;
                         union in_addr_union address;
+                        sd_id128_t id128;
                 } buffer;
 
                 switch (type) {
@@ -778,6 +799,10 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
                         data = va_arg(ap, const char *);
                         break;
 
+                case TABLE_STRV:
+                        data = va_arg(ap, char * const *);
+                        break;
+
                 case TABLE_BOOLEAN:
                         buffer.b = va_arg(ap, int);
                         data = &buffer.b;
@@ -884,6 +909,12 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
                         data = &buffer.address.in6;
                         break;
 
+                case TABLE_UUID:
+                case TABLE_ID128:
+                        buffer.id128 = va_arg(ap, sd_id128_t);
+                        data = &buffer.id128;
+                        break;
+
                 case TABLE_SET_MINIMUM_WIDTH: {
                         size_t w = va_arg(ap, size_t);
 
@@ -1055,6 +1086,9 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
                 case TABLE_PATH:
                         return path_compare(a->string, b->string);
 
+                case TABLE_STRV:
+                        return strv_compare(a->strv, b->strv);
+
                 case TABLE_BOOLEAN:
                         if (!a->boolean && b->boolean)
                                 return -1;
@@ -1117,6 +1151,10 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
                 case TABLE_IN6_ADDR:
                         return memcmp(&a->address.in6, &b->address.in6, FAMILY_ADDRESS_SIZE(AF_INET6));
 
+                case TABLE_UUID:
+                case TABLE_ID128:
+                        return memcmp(&a->id128, &b->id128, sizeof(sd_id128_t));
+
                 default:
                         ;
                 }
@@ -1185,6 +1223,17 @@ static const char *table_data_format(Table *t, TableData *d) {
 
                 return d->string;
 
+        case TABLE_STRV: {
+                char *p;
+
+                p = strv_join(d->strv, "\n");
+                if (!p)
+                        return NULL;
+
+                d->formatted = p;
+                break;
+        }
+
         case TABLE_BOOLEAN:
                 return yes_no(d->boolean);
 
@@ -1420,6 +1469,28 @@ static const char *table_data_format(Table *t, TableData *d) {
                 break;
         }
 
+        case TABLE_ID128: {
+                char *p;
+
+                p = new(char, SD_ID128_STRING_MAX);
+                if (!p)
+                        return NULL;
+
+                d->formatted = sd_id128_to_string(d->id128, p);
+                break;
+        }
+
+        case TABLE_UUID: {
+                char *p;
+
+                p = new(char, ID128_UUID_STRING_MAX);
+                if (!p)
+                        return NULL;
+
+                d->formatted = id128_to_uuid_string(d->id128, p);
+                break;
+        }
+
         default:
                 assert_not_reached("Unexpected type?");
         }
@@ -2054,6 +2125,9 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
         case TABLE_PATH:
                 return json_variant_new_string(ret, d->string);
 
+        case TABLE_STRV:
+                return json_variant_new_array_strv(ret, d->strv);
+
         case TABLE_BOOLEAN:
                 return json_variant_new_boolean(ret, d->boolean);
 
@@ -2121,6 +2195,16 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
         case TABLE_IN6_ADDR:
                 return json_variant_new_array_bytes(ret, &d->address, FAMILY_ADDRESS_SIZE(AF_INET6));
 
+        case TABLE_ID128: {
+                char buf[SD_ID128_STRING_MAX];
+                return json_variant_new_string(ret, sd_id128_to_string(d->id128, buf));
+        }
+
+        case TABLE_UUID: {
+                char buf[ID128_UUID_STRING_MAX];
+                return json_variant_new_string(ret, id128_to_uuid_string(d->id128, buf));
+        }
+
         default:
                 return -EINVAL;
         }
index cb9232b47ada5425543bbe19f6c84e45adec9561..870a29d3856cb5c67fadc84de5824e5d21a099d5 100644 (file)
@@ -11,6 +11,7 @@
 typedef enum TableDataType {
         TABLE_EMPTY,
         TABLE_STRING,
+        TABLE_STRV,
         TABLE_PATH,
         TABLE_BOOLEAN,
         TABLE_TIMESTAMP,
@@ -34,6 +35,8 @@ typedef enum TableDataType {
         TABLE_IFINDEX,
         TABLE_IN_ADDR,  /* Takes a union in_addr_union (or a struct in_addr) */
         TABLE_IN6_ADDR, /* Takes a union in_addr_union (or a struct in6_addr) */
+        TABLE_ID128,
+        TABLE_UUID,
         _TABLE_DATA_TYPE_MAX,
 
         /* The following are not really data types, but commands for table_add_cell_many() to make changes to
index 06e1ab803125d342fc8db4569930b0e7001fc480..acdd0096f1419391bae148a8a041928b4a8cd822 100644 (file)
@@ -493,15 +493,21 @@ int generator_hook_up_growfs(
                 "BindsTo=%%i.mount\n"
                 "Conflicts=shutdown.target\n"
                 "After=%%i.mount\n"
-                "Before=shutdown.target %s\n"
+                "Before=shutdown.target %s\n",
+                program_invocation_short_name,
+                target);
+
+        if (empty_or_root(where)) /* Make sure the root fs is actually writable before we resize it */
+                fprintf(f,
+                        "After=systemd-remount-fs.service\n");
+
+        fprintf(f,
                 "\n"
                 "[Service]\n"
                 "Type=oneshot\n"
                 "RemainAfterExit=yes\n"
                 "ExecStart="SYSTEMD_GROWFS_PATH " %s\n"
                 "TimeoutSec=0\n",
-                program_invocation_short_name,
-                target,
                 escaped);
 
         return generator_add_symlink(dir, where_unit, "wants", unit);
@@ -513,6 +519,103 @@ int generator_enable_remount_fs_service(const char *dir) {
                                      SYSTEM_DATA_UNIT_PATH "/" SPECIAL_REMOUNT_FS_SERVICE);
 }
 
+int generator_write_blockdev_dependency(
+                FILE *f,
+                const char *what) {
+
+        _cleanup_free_ char *escaped = NULL;
+        int r;
+
+        assert(f);
+        assert(what);
+
+        if (!path_startswith(what, "/dev/"))
+                return 0;
+
+        r = unit_name_path_escape(what, &escaped);
+        if (r < 0)
+                return log_error_errno(r, "Failed to escape device node path %s: %m", what);
+
+        fprintf(f,
+                "After=blockdev@%s.target\n",
+                escaped);
+
+        return 0;
+}
+
+int generator_write_cryptsetup_unit_section(
+                FILE *f,
+                const char *source) {
+
+        assert(f);
+
+        fprintf(f,
+                "[Unit]\n"
+                "Description=Cryptography Setup for %%I\n"
+                "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n");
+
+        if (source)
+                fprintf(f, "SourcePath=%s\n", source);
+
+        fprintf(f,
+                "DefaultDependencies=no\n"
+                "IgnoreOnIsolate=true\n"
+                "After=cryptsetup-pre.target\n"
+                "Before=blockdev@dev-mapper-%%i.target\n"
+                "Wants=blockdev@dev-mapper-%%i.target\n");
+
+        return 0;
+}
+
+int generator_write_cryptsetup_service_section(
+                FILE *f,
+                const char *name,
+                const char *what,
+                const char *password,
+                const char *options) {
+
+        _cleanup_free_ char *name_escaped = NULL, *what_escaped = NULL, *password_escaped = NULL, *options_escaped = NULL;
+
+        assert(f);
+        assert(name);
+        assert(what);
+
+        name_escaped = specifier_escape(name);
+        if (!name_escaped)
+                return log_oom();
+
+        what_escaped = specifier_escape(what);
+        if (!what_escaped)
+                return log_oom();
+
+        if (password) {
+                password_escaped = specifier_escape(password);
+                if (!password_escaped)
+                        return log_oom();
+        }
+
+        if (options) {
+                options_escaped = specifier_escape(options);
+                if (!options_escaped)
+                        return log_oom();
+        }
+
+        fprintf(f,
+                "\n"
+                "[Service]\n"
+                "Type=oneshot\n"
+                "RemainAfterExit=yes\n"
+                "TimeoutSec=0\n"          /* The binary handles timeouts on its own */
+                "KeyringMode=shared\n"    /* Make sure we can share cached keys among instances */
+                "OOMScoreAdjust=500\n"    /* Unlocking can allocate a lot of memory if Argon2 is used */
+                "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
+                "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
+                name_escaped, what_escaped, strempty(password_escaped), strempty(options_escaped),
+                name_escaped);
+
+        return 0;
+}
+
 void log_setup_generator(void) {
         log_set_prohibit_ipc(true);
         log_setup_service();
index fa002a91143adbfd4c78841d6df5c6745f48126b..579e291fe8ab3d070a6bc3bce15b0f5f2a33fb93 100644 (file)
@@ -27,6 +27,21 @@ int generator_write_timeouts(
         const char *opts,
         char **filtered);
 
+int generator_write_blockdev_dependency(
+                FILE *f,
+                const char *what);
+
+int generator_write_cryptsetup_unit_section(
+                FILE *f,
+                const char *source);
+
+int generator_write_cryptsetup_service_section(
+                FILE *f,
+                const char *name,
+                const char *what,
+                const char *password,
+                const char *options);
+
 int generator_write_device_deps(
         const char *dir,
         const char *what,
diff --git a/src/shared/gpt.c b/src/shared/gpt.c
new file mode 100644 (file)
index 0000000..e62f21e
--- /dev/null
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "gpt.h"
+#include "string-util.h"
+
+const GptPartitionType gpt_partition_type_table[] = {
+        { GPT_ROOT_X86,              "root-x86"              },
+        { GPT_ROOT_X86_VERITY,       "root-x86-verity"       },
+        { GPT_ROOT_X86_64,           "root-x86-64"           },
+        { GPT_ROOT_X86_64_VERITY,    "root-x86-64-verity"    },
+        { GPT_ROOT_ARM,              "root-arm"              },
+        { GPT_ROOT_ARM_VERITY,       "root-arm-verity"       },
+        { GPT_ROOT_ARM_64,           "root-arm64"            },
+        { GPT_ROOT_ARM_64_VERITY,    "root-arm64-verity"     },
+        { GPT_ROOT_IA64,             "root-ia64"             },
+        { GPT_ROOT_IA64_VERITY,      "root-ia64-verity"      },
+#ifdef GPT_ROOT_NATIVE
+        { GPT_ROOT_NATIVE,           "root"                  },
+        { GPT_ROOT_NATIVE_VERITY,    "root-verity"           },
+#endif
+#ifdef GPT_ROOT_SECONDARY
+        { GPT_ROOT_SECONDARY,        "root-secondary"        },
+        { GPT_ROOT_SECONDARY_VERITY, "root-secondary-verity" },
+#endif
+        { GPT_ESP,                   "esp"                   },
+        { GPT_XBOOTLDR,              "xbootldr"              },
+        { GPT_SWAP,                  "swap"                  },
+        { GPT_HOME,                  "home"                  },
+        { GPT_SRV,                   "srv"                   },
+        { GPT_VAR,                   "var"                   },
+        { GPT_TMP,                   "tmp"                   },
+        { GPT_LINUX_GENERIC,         "linux-generic",        },
+        {}
+};
+
+const char *gpt_partition_type_uuid_to_string(sd_id128_t id) {
+        for (size_t i = 0; i < ELEMENTSOF(gpt_partition_type_table) - 1; i++)
+                if (sd_id128_equal(id, gpt_partition_type_table[i].uuid))
+                        return gpt_partition_type_table[i].name;
+
+        return NULL;
+}
+
+const char *gpt_partition_type_uuid_to_string_harder(
+                sd_id128_t id,
+                char buffer[static ID128_UUID_STRING_MAX]) {
+
+        const char *s;
+
+        assert(buffer);
+
+        s = gpt_partition_type_uuid_to_string(id);
+        if (s)
+                return s;
+
+        return id128_to_uuid_string(id, buffer);
+}
+
+int gpt_partition_type_uuid_from_string(const char *s, sd_id128_t *ret) {
+        assert(s);
+        assert(ret);
+
+        for (size_t i = 0; i < ELEMENTSOF(gpt_partition_type_table) - 1; i++)
+                if (streq(s, gpt_partition_type_table[i].name)) {
+                        *ret = gpt_partition_type_table[i].uuid;
+                        return 0;
+                }
+
+        return sd_id128_from_string(s, ret);
+}
index 31e01bd5a5cc9afd2cf87fce25c34de7f83b5528..26a4e1ea653c5d8bb7cb97f0e190b8804c455111 100644 (file)
@@ -5,6 +5,8 @@
 
 #include "sd-id128.h"
 
+#include "id128-util.h"
+
 /* We only support root disk discovery for x86, x86-64, Itanium and ARM for
  * now, since EFI for anything else doesn't really exist, and we only
  * care for root partitions on the same disk as the EFI ESP. */
@@ -19,6 +21,9 @@
 #define GPT_SWAP        SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f)
 #define GPT_HOME        SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15)
 #define GPT_SRV         SD_ID128_MAKE(3b,8f,84,25,20,e0,4f,3b,90,7f,1a,25,a7,6f,98,e8)
+#define GPT_VAR         SD_ID128_MAKE(4d,21,b0,16,b5,34,45,c2,a9,fb,5c,16,e0,91,fd,2d)
+#define GPT_TMP         SD_ID128_MAKE(7e,c6,f5,57,3b,c5,4a,ca,b2,93,16,ef,5d,f6,39,d1)
+#define GPT_USER_HOME   SD_ID128_MAKE(77,3f,91,ef,66,d4,49,b5,bd,83,d6,83,bf,40,ad,16)
 
 /* Verity partitions for the root partitions above (we only define them for the root partitions, because only they are
  * are commonly read-only and hence suitable for verity). */
@@ -53,7 +58,9 @@
 #  define GPT_ROOT_NATIVE_VERITY GPT_ROOT_ARM_VERITY
 #endif
 
+#define GPT_FLAG_REQUIRED_PARTITION (1ULL << 0)
 #define GPT_FLAG_NO_BLOCK_IO_PROTOCOL (1ULL << 1)
+#define GPT_FLAG_LEGACY_BIOS_BOOTABLE (1ULL << 2)
 
 /* Flags we recognize on the root, swap, home and srv partitions when
  * doing auto-discovery. These happen to be identical to what
 #define GPT_FLAG_NO_AUTO (1ULL << 63)
 
 #define GPT_LINUX_GENERIC SD_ID128_MAKE(0f,c6,3d,af,84,83,47,72,8e,79,3d,69,d8,47,7d,e4)
+
+const char *gpt_partition_type_uuid_to_string(sd_id128_t id);
+const char *gpt_partition_type_uuid_to_string_harder(
+                sd_id128_t id,
+                char buffer[static ID128_UUID_STRING_MAX]);
+int gpt_partition_type_uuid_from_string(const char *s, sd_id128_t *ret);
+
+typedef struct GptPartitionType {
+        sd_id128_t uuid;
+        const char *name;
+} GptPartitionType;
+
+extern const GptPartitionType gpt_partition_type_table[];
diff --git a/src/shared/group-record-nss.c b/src/shared/group-record-nss.c
new file mode 100644 (file)
index 0000000..77924f1
--- /dev/null
@@ -0,0 +1,203 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "errno-util.h"
+#include "group-record-nss.h"
+#include "libcrypt-util.h"
+#include "strv.h"
+
+int nss_group_to_group_record(
+                const struct group *grp,
+                const struct sgrp *sgrp,
+                GroupRecord **ret) {
+
+        _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
+        int r;
+
+        assert(grp);
+        assert(ret);
+
+        if (isempty(grp->gr_name))
+                return -EINVAL;
+
+        if (sgrp && !streq_ptr(sgrp->sg_namp, grp->gr_name))
+                return -EINVAL;
+
+        g = group_record_new();
+        if (!g)
+                return -ENOMEM;
+
+        g->group_name = strdup(grp->gr_name);
+        if (!g->group_name)
+                return -ENOMEM;
+
+        g->members = strv_copy(grp->gr_mem);
+        if (!g->members)
+                return -ENOMEM;
+
+        g->gid = grp->gr_gid;
+
+        if (sgrp) {
+                if (hashed_password_valid(sgrp->sg_passwd)) {
+                        g->hashed_password = strv_new(sgrp->sg_passwd);
+                        if (!g->hashed_password)
+                                return -ENOMEM;
+                }
+
+                r = strv_extend_strv(&g->members, sgrp->sg_mem, 1);
+                if (r < 0)
+                        return r;
+
+                g->administrators = strv_copy(sgrp->sg_adm);
+                if (!g->administrators)
+                        return -ENOMEM;
+        }
+
+        r = json_build(&g->json, JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(g->group_name)),
+                                       JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(g->gid)),
+                                       JSON_BUILD_PAIR_CONDITION(!strv_isempty(g->members), "members", JSON_BUILD_STRV(g->members)),
+                                       JSON_BUILD_PAIR_CONDITION(!strv_isempty(g->hashed_password), "privileged", JSON_BUILD_OBJECT(JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRV(g->hashed_password)))),
+                                       JSON_BUILD_PAIR_CONDITION(!strv_isempty(g->administrators), "administrators", JSON_BUILD_STRV(g->administrators))));
+        if (r < 0)
+                return r;
+
+        g->mask = USER_RECORD_REGULAR |
+                (!strv_isempty(g->hashed_password) ? USER_RECORD_PRIVILEGED : 0);
+
+        *ret = TAKE_PTR(g);
+        return 0;
+}
+
+int nss_sgrp_for_group(const struct group *grp, struct sgrp *ret_sgrp, char **ret_buffer) {
+        size_t buflen = 4096;
+        int r;
+
+        assert(grp);
+        assert(ret_sgrp);
+        assert(ret_buffer);
+
+        for (;;) {
+                _cleanup_free_ char *buf = NULL;
+                struct sgrp sgrp, *result;
+
+                buf = malloc(buflen);
+                if (!buf)
+                        return -ENOMEM;
+
+                r = getsgnam_r(grp->gr_name, &sgrp, buf, buflen, &result);
+                if (r == 0) {
+                        if (!result)
+                                return -ESRCH;
+
+                        *ret_sgrp = *result;
+                        *ret_buffer = TAKE_PTR(buf);
+                        return 0;
+                }
+                if (r < 0)
+                        return -EIO; /* Weird, this should not return negative! */
+                if (r != ERANGE)
+                        return -r;
+
+                if (buflen > SIZE_MAX / 2)
+                        return -ERANGE;
+
+                buflen *= 2;
+                buf = mfree(buf);
+        }
+}
+
+int nss_group_record_by_name(const char *name, GroupRecord **ret) {
+        _cleanup_free_ char *buf = NULL, *sbuf = NULL;
+        struct group grp, *result;
+        bool incomplete = false;
+        size_t buflen = 4096;
+        struct sgrp sgrp;
+        int r;
+
+        assert(name);
+        assert(ret);
+
+        for (;;) {
+                buf = malloc(buflen);
+                if (!buf)
+                        return -ENOMEM;
+
+                r = getgrnam_r(name, &grp, buf, buflen, &result);
+                if (r == 0)  {
+                        if (!result)
+                                return -ESRCH;
+
+                        break;
+                }
+
+                if (r < 0)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EIO), "getgrnam_r() returned a negative value");
+                if (r != ERANGE)
+                        return -r;
+                if (buflen > SIZE_MAX / 2)
+                        return -ERANGE;
+
+                buflen *= 2;
+                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 (r < 0)
+                return r;
+
+        (*ret)->incomplete = incomplete;
+        return 0;
+}
+
+int nss_group_record_by_gid(gid_t gid, GroupRecord **ret) {
+        _cleanup_free_ char *buf = NULL, *sbuf = NULL;
+        struct group grp, *result;
+        bool incomplete = false;
+        size_t buflen = 4096;
+        struct sgrp sgrp;
+        int r;
+
+        assert(ret);
+
+        for (;;) {
+                buf = malloc(buflen);
+                if (!buf)
+                        return -ENOMEM;
+
+                r = getgrgid_r(gid, &grp, buf, buflen, &result);
+                if (r == 0)  {
+                        if (!result)
+                                return -ESRCH;
+                        break;
+                }
+
+                if (r < 0)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EIO), "getgrgid_r() returned a negative value");
+                if (r != ERANGE)
+                        return -r;
+                if (buflen > SIZE_MAX / 2)
+                        return -ERANGE;
+
+                buflen *= 2;
+                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 (r < 0)
+                return r;
+
+        (*ret)->incomplete = incomplete;
+        return 0;
+}
diff --git a/src/shared/group-record-nss.h b/src/shared/group-record-nss.h
new file mode 100644 (file)
index 0000000..38b2995
--- /dev/null
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <grp.h>
+#include <gshadow.h>
+
+#include "group-record.h"
+
+/* Synthesize GroupRecord objects from NSS data */
+
+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);
diff --git a/src/shared/group-record-show.c b/src/shared/group-record-show.c
new file mode 100644 (file)
index 0000000..d0300e4
--- /dev/null
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "format-util.h"
+#include "group-record-show.h"
+#include "strv.h"
+#include "user-util.h"
+#include "userdb.h"
+
+void group_record_show(GroupRecord *gr, bool show_full_user_info) {
+        int r;
+
+        printf("  Group name: %s\n",
+               group_record_group_name_and_realm(gr));
+
+        printf(" Disposition: %s\n", user_disposition_to_string(group_record_disposition(gr)));
+
+        if (gr->last_change_usec != USEC_INFINITY) {
+                char buf[FORMAT_TIMESTAMP_MAX];
+                printf(" Last Change: %s\n", format_timestamp(buf, sizeof(buf), gr->last_change_usec));
+        }
+
+        if (gid_is_valid(gr->gid))
+                printf("         GID: " GID_FMT "\n", gr->gid);
+
+        if (show_full_user_info) {
+                _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+
+                r = membershipdb_by_group(gr->group_name, 0, &iterator);
+                if (r < 0) {
+                        errno = -r;
+                        printf("     Members: (can't acquire: %m)");
+                } else {
+                        const char *prefix = "     Members:";
+
+                        for (;;) {
+                                _cleanup_free_ char *user = NULL;
+
+                                r = membershipdb_iterator_get(iterator, &user, NULL);
+                                if (r == -ESRCH)
+                                        break;
+                                if (r < 0) {
+                                        errno = -r;
+                                        printf("%s (can't iterate: %m\n", prefix);
+                                        break;
+                                }
+
+                                printf("%s %s\n", prefix, user);
+                                prefix = "             ";
+                        }
+                }
+        } else {
+                const char *prefix = "     Members:";
+                char **i;
+
+                STRV_FOREACH(i, gr->members) {
+                        printf("%s %s\n", prefix, *i);
+                        prefix = "             ";
+                }
+        }
+
+        if (!strv_isempty(gr->administrators)) {
+                const char *prefix = "      Admins:";
+                char **i;
+
+                STRV_FOREACH(i, gr->administrators) {
+                        printf("%s %s\n", prefix, *i);
+                        prefix = "             ";
+                }
+        }
+
+        if (!strv_isempty(gr->hashed_password))
+                printf("   Passwords: %zu\n", strv_length(gr->hashed_password));
+
+        if (gr->service)
+                printf("     Service: %s\n", gr->service);
+}
diff --git a/src/shared/group-record-show.h b/src/shared/group-record-show.h
new file mode 100644 (file)
index 0000000..12bdbd1
--- /dev/null
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "group-record.h"
+
+void group_record_show(GroupRecord *gr, bool show_full_user_info);
diff --git a/src/shared/group-record.c b/src/shared/group-record.c
new file mode 100644 (file)
index 0000000..d5ec32f
--- /dev/null
@@ -0,0 +1,346 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "group-record.h"
+#include "strv.h"
+#include "user-util.h"
+
+GroupRecord* group_record_new(void) {
+        GroupRecord *h;
+
+        h = new(GroupRecord, 1);
+        if (!h)
+                return NULL;
+
+        *h = (GroupRecord) {
+                .n_ref = 1,
+                .disposition = _USER_DISPOSITION_INVALID,
+                .last_change_usec = UINT64_MAX,
+                .gid = GID_INVALID,
+        };
+
+        return h;
+}
+
+static GroupRecord *group_record_free(GroupRecord *g) {
+        if (!g)
+                return NULL;
+
+        free(g->group_name);
+        free(g->realm);
+        free(g->group_name_and_realm_auto);
+
+        strv_free(g->members);
+        free(g->service);
+        strv_free(g->administrators);
+        strv_free_erase(g->hashed_password);
+
+        json_variant_unref(g->json);
+
+        return mfree(g);
+}
+
+DEFINE_TRIVIAL_REF_UNREF_FUNC(GroupRecord, group_record, group_record_free);
+
+static int dispatch_privileged(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+
+        static const JsonDispatch privileged_dispatch_table[] = {
+                { "hashedPassword", _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(GroupRecord, hashed_password), JSON_SAFE },
+                {},
+        };
+
+        return json_dispatch(variant, privileged_dispatch_table, NULL, flags, userdata);
+}
+
+static int dispatch_binding(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+
+        static const JsonDispatch binding_dispatch_table[] = {
+                { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(GroupRecord, gid), 0 },
+                {},
+        };
+
+        char smid[SD_ID128_STRING_MAX];
+        JsonVariant *m;
+        sd_id128_t mid;
+        int r;
+
+        if (!variant)
+                return 0;
+
+        if (!json_variant_is_object(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
+
+        r = sd_id128_get_machine(&mid);
+        if (r < 0)
+                return json_log(variant, flags, r, "Failed to determine machine ID: %m");
+
+        m = json_variant_by_key(variant, sd_id128_to_string(mid, smid));
+        if (!m)
+                return 0;
+
+        return json_dispatch(m, binding_dispatch_table, NULL, flags, userdata);
+}
+
+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         },
+                { "gid",            JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,          offsetof(GroupRecord, gid),            0         },
+                { "members",        JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,  offsetof(GroupRecord, members),        0         },
+                { "administrators", JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,  offsetof(GroupRecord, administrators), 0         },
+                {},
+        };
+
+        JsonVariant *e;
+        int r;
+
+        if (!variant)
+                return 0;
+
+        if (!json_variant_is_array(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
+
+        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
+                bool matching = false;
+                JsonVariant *m;
+
+                if (!json_variant_is_object(e))
+                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of objects.", strna(name));
+
+                m = json_variant_by_key(e, "matchMachineId");
+                if (m) {
+                        r = per_machine_id_match(m, flags);
+                        if (r < 0)
+                                return r;
+
+                        matching = r > 0;
+                }
+
+                if (!matching) {
+                        m = json_variant_by_key(e, "matchHostname");
+                        if (m) {
+                                r = per_machine_hostname_match(m, flags);
+                                if (r < 0)
+                                        return r;
+
+                                matching = r > 0;
+                        }
+                }
+
+                if (!matching)
+                        continue;
+
+                r = json_dispatch(e, per_machine_dispatch_table, NULL, flags, userdata);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int dispatch_status(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+
+        static const JsonDispatch status_dispatch_table[] = {
+                { "service", JSON_VARIANT_STRING, json_dispatch_string, offsetof(GroupRecord, service), JSON_SAFE },
+                {},
+        };
+
+        char smid[SD_ID128_STRING_MAX];
+        JsonVariant *m;
+        sd_id128_t mid;
+        int r;
+
+        if (!variant)
+                return 0;
+
+        if (!json_variant_is_object(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
+
+        r = sd_id128_get_machine(&mid);
+        if (r < 0)
+                return json_log(variant, flags, r, "Failed to determine machine ID: %m");
+
+        m = json_variant_by_key(variant, sd_id128_to_string(mid, smid));
+        if (!m)
+                return 0;
+
+        return json_dispatch(m, status_dispatch_table, NULL, flags, userdata);
+}
+
+static int group_record_augment(GroupRecord *h, JsonDispatchFlags json_flags) {
+        assert(h);
+
+        if (!FLAGS_SET(h->mask, USER_RECORD_REGULAR))
+                return 0;
+
+        assert(h->group_name);
+
+        if (!h->group_name_and_realm_auto && h->realm) {
+                h->group_name_and_realm_auto = strjoin(h->group_name, "@", h->realm);
+                if (!h->group_name_and_realm_auto)
+                        return json_log_oom(h->json, json_flags);
+        }
+
+        return 0;
+}
+
+int group_record_load(
+                GroupRecord *h,
+                JsonVariant *v,
+                UserRecordLoadFlags load_flags) {
+
+        static const JsonDispatch group_dispatch_table[] = {
+                { "groupName",      JSON_VARIANT_STRING,   json_dispatch_user_group_name,  offsetof(GroupRecord, group_name),       0         },
+                { "realm",          JSON_VARIANT_STRING,   json_dispatch_realm,            offsetof(GroupRecord, realm),            0         },
+                { "disposition",    JSON_VARIANT_STRING,   json_dispatch_user_disposition, offsetof(GroupRecord, disposition),      0         },
+                { "service",        JSON_VARIANT_STRING,   json_dispatch_string,           offsetof(GroupRecord, service),          JSON_SAFE },
+                { "lastChangeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64,           offsetof(GroupRecord, last_change_usec), 0         },
+                { "gid",            JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid,          offsetof(GroupRecord, gid),              0         },
+                { "members",        JSON_VARIANT_ARRAY,    json_dispatch_user_group_list,  offsetof(GroupRecord, members),          0         },
+                { "administrators", JSON_VARIANT_ARRAY,    json_dispatch_user_group_list,  offsetof(GroupRecord, administrators),   0         },
+
+                { "privileged",     JSON_VARIANT_OBJECT,   dispatch_privileged,            0,                                       0         },
+
+                /* Not defined for now, for groups, but let's at least generate sensible errors about it */
+                { "secret",         JSON_VARIANT_OBJECT,   json_dispatch_unsupported,      0,                                       0         },
+
+                /* Ignore the perMachine, binding and 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         },
+
+                /* Ignore 'signature', we check it with explicit accessors instead */
+                { "signature",      JSON_VARIANT_ARRAY,    NULL,                           0,                                       0          },
+                {},
+        };
+
+        JsonDispatchFlags json_flags = USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(load_flags);
+        int r;
+
+        assert(h);
+        assert(!h->json);
+
+        /* Note that this call will leave a half-initialized record around on failure! */
+
+        if ((USER_RECORD_REQUIRE_MASK(load_flags) & (USER_RECORD_SECRET|USER_RECORD_PRIVILEGED)))
+                return json_log(v, json_flags, SYNTHETIC_ERRNO(EINVAL), "Secret and privileged section currently not available for groups, refusing.");
+
+        r = user_group_record_mangle(v, load_flags, &h->json, &h->mask);
+        if (r < 0)
+                return r;
+
+        r = json_dispatch(h->json, group_dispatch_table, NULL, json_flags, h);
+        if (r < 0)
+                return r;
+
+        /* During the parsing operation above we ignored the 'perMachine', 'binding' and 'status' fields, since we want
+         * them to override the global options. Let's process them now. */
+
+        r = dispatch_per_machine("perMachine", json_variant_by_key(h->json, "perMachine"), json_flags, h);
+        if (r < 0)
+                return r;
+
+        r = dispatch_binding("binding", json_variant_by_key(h->json, "binding"), json_flags, h);
+        if (r < 0)
+                return r;
+
+        r = dispatch_status("status", json_variant_by_key(h->json, "status"), json_flags, h);
+        if (r < 0)
+                return r;
+
+        if (FLAGS_SET(h->mask, USER_RECORD_REGULAR) && !h->group_name)
+                return json_log(h->json, json_flags, SYNTHETIC_ERRNO(EINVAL), "Group name field missing, refusing.");
+
+        r = group_record_augment(h, json_flags);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+int group_record_build(GroupRecord **ret, ...) {
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
+        va_list ap;
+        int r;
+
+        assert(ret);
+
+        va_start(ap, ret);
+        r = json_buildv(&v, ap);
+        va_end(ap);
+
+        if (r < 0)
+                return r;
+
+        g = group_record_new();
+        if (!g)
+                return -ENOMEM;
+
+        r = group_record_load(g, v, USER_RECORD_LOAD_FULL);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(g);
+        return 0;
+}
+
+const char *group_record_group_name_and_realm(GroupRecord *h) {
+        assert(h);
+
+        /* Return the pre-initialized joined string if it is defined */
+        if (h->group_name_and_realm_auto)
+                return h->group_name_and_realm_auto;
+
+        /* If it's not defined then we cannot have a realm */
+        assert(!h->realm);
+        return h->group_name;
+}
+
+UserDisposition group_record_disposition(GroupRecord *h) {
+        assert(h);
+
+        if (h->disposition >= 0)
+                return h->disposition;
+
+        /* If not declared, derive from GID */
+
+        if (!gid_is_valid(h->gid))
+                return _USER_DISPOSITION_INVALID;
+
+        if (h->gid == 0 || h->gid == GID_NOBODY)
+                return USER_INTRINSIC;
+
+        if (gid_is_system(h->gid))
+                return USER_SYSTEM;
+
+        if (gid_is_dynamic(h->gid))
+                return USER_DYNAMIC;
+
+        if (gid_is_container(h->gid))
+                return USER_CONTAINER;
+
+        if (h->gid > INT32_MAX)
+                return USER_RESERVED;
+
+        return USER_REGULAR;
+}
+
+int group_record_clone(GroupRecord *h, UserRecordLoadFlags flags, GroupRecord **ret) {
+        _cleanup_(group_record_unrefp) GroupRecord *c = NULL;
+        int r;
+
+        assert(h);
+        assert(ret);
+
+        c = group_record_new();
+        if (!c)
+                return -ENOMEM;
+
+        r = group_record_load(c, h->json, flags);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(c);
+        return 0;
+}
diff --git a/src/shared/group-record.h b/src/shared/group-record.h
new file mode 100644 (file)
index 0000000..b72a43e
--- /dev/null
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "json.h"
+#include "user-record.h"
+
+typedef struct GroupRecord {
+        unsigned n_ref;
+        UserRecordMask mask;
+        bool incomplete;
+
+        char *group_name;
+        char *realm;
+        char *group_name_and_realm_auto;
+
+        UserDisposition disposition;
+        uint64_t last_change_usec;
+
+        gid_t gid;
+
+        char **members;
+
+        char *service;
+
+        /* The following exist mostly so that we can cover the full /etc/gshadow set of fields, we currently
+         * do not actually make use of these */
+        char **administrators;  /* maps to 'struct sgrp' .sg_adm field */
+        char **hashed_password; /* maps to 'struct sgrp' .sg_passwd field */
+
+        JsonVariant *json;
+} GroupRecord;
+
+GroupRecord* group_record_new(void);
+GroupRecord* group_record_ref(GroupRecord *g);
+GroupRecord* group_record_unref(GroupRecord *g);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(GroupRecord*, group_record_unref);
+
+int group_record_load(GroupRecord *h, JsonVariant *v, UserRecordLoadFlags flags);
+int group_record_build(GroupRecord **ret, ...);
+int group_record_clone(GroupRecord *g, UserRecordLoadFlags flags, GroupRecord **ret);
+
+const char *group_record_group_name_and_realm(GroupRecord *h);
+UserDisposition group_record_disposition(GroupRecord *h);
index 356f41050786ce67558db18e618039287f50f8d4..6237424e8243aec252eb4f33e973b22e32b6e08c 100644 (file)
 #include "pretty-print.h"
 #include "terminal-util.h"
 
-int id128_pretty_print(sd_id128_t id, Id128PrettyPrintMode mode) {
-        _cleanup_free_ char *man_link = NULL, *mod_link = NULL;
+int id128_pretty_print_sample(const char *name, sd_id128_t id) {
+       _cleanup_free_ char *man_link = NULL, *mod_link = NULL;
         const char *on, *off;
         unsigned i;
 
-        assert(mode >= 0);
-        assert(mode < _ID128_PRETTY_PRINT_MODE_MAX);
-
-        if (mode == ID128_PRINT_ID128) {
-                printf(SD_ID128_FORMAT_STR "\n",
-                       SD_ID128_FORMAT_VAL(id));
-                return 0;
-        } else if (mode == ID128_PRINT_UUID) {
-                printf(SD_ID128_UUID_FORMAT_STR "\n",
-                       SD_ID128_FORMAT_VAL(id));
-                return 0;
-        }
-
         on = ansi_highlight();
         off = ansi_normal();
 
@@ -42,24 +29,41 @@ int id128_pretty_print(sd_id128_t id, Id128PrettyPrintMode mode) {
                "As UUID:\n"
                "%s" SD_ID128_UUID_FORMAT_STR "%s\n\n"
                "As %s macro:\n"
-               "%s#define XYZ SD_ID128_MAKE(",
+               "%s#define %s SD_ID128_MAKE(",
                on, SD_ID128_FORMAT_VAL(id), off,
                on, SD_ID128_FORMAT_VAL(id), off,
                man_link,
-               on);
+               on, name);
         for (i = 0; i < 16; i++)
                 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
         printf(")%s\n\n", off);
 
         printf("As Python constant:\n"
                ">>> import %s\n"
-               ">>> %sXYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')%s\n",
+               ">>> %s%s = uuid.UUID('" SD_ID128_FORMAT_STR "')%s\n",
                mod_link,
-               on, SD_ID128_FORMAT_VAL(id), off);
+               on, name, SD_ID128_FORMAT_VAL(id), off);
 
         return 0;
 }
 
+
+int id128_pretty_print(sd_id128_t id, Id128PrettyPrintMode mode) {
+        assert(mode >= 0);
+        assert(mode < _ID128_PRETTY_PRINT_MODE_MAX);
+
+        if (mode == ID128_PRINT_ID128) {
+                printf(SD_ID128_FORMAT_STR "\n",
+                       SD_ID128_FORMAT_VAL(id));
+                return 0;
+        } else if (mode == ID128_PRINT_UUID) {
+                printf(SD_ID128_UUID_FORMAT_STR "\n",
+                       SD_ID128_FORMAT_VAL(id));
+                return 0;
+        } else
+                return id128_pretty_print_sample("XYZ", id);
+}
+
 int id128_print_new(Id128PrettyPrintMode mode) {
         sd_id128_t id;
         int r;
index 1dc5b6aae57b00b7d6b91ce3bc15fa425e718822..247558231c9a26c1eaeb382d0df03236817ebb65 100644 (file)
@@ -14,5 +14,6 @@ typedef enum Id128PrettyPrintMode {
         _ID128_PRETTY_PRINT_MODE_INVALID = -1
 } Id128PrettyPrintMode;
 
+int id128_pretty_print_sample(const char *name, sd_id128_t id);
 int id128_pretty_print(sd_id128_t id, Id128PrettyPrintMode mode);
 int id128_print_new(Id128PrettyPrintMode mode);
index f7770e7df50aea23fc799a426304295fea624106..0a57c0899090fd6d1ada682e95a48e2b329e43c1 100644 (file)
@@ -4,6 +4,8 @@
 
 #include "alloc-util.h"
 #include "btrfs-util.h"
+#include "chattr-util.h"
+#include "errno-util.h"
 #include "import-util.h"
 #include "log.h"
 #include "macro.h"
@@ -163,3 +165,15 @@ int import_assign_pool_quota_and_warn(const char *path) {
 
         return 0;
 }
+
+int import_set_nocow_and_log(int fd, const char *path) {
+        int r;
+
+        r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
+        if (r < 0)
+                return log_full_errno(
+                                ERRNO_IS_NOT_SUPPORTED(r) ? LOG_DEBUG : LOG_WARNING,
+                                r, "Failed to set file attributes on %s: %m", path);
+
+        return 0;
+}
index 0f2a5170c85ca9a0ec9011f70cf6bc705a3ce6e3..d85aa565cc1e5bd089f28a158b22eaf9507c187f 100644 (file)
@@ -23,3 +23,5 @@ int tar_strip_suffixes(const char *name, char **ret);
 int raw_strip_suffixes(const char *name, char **ret);
 
 int import_assign_pool_quota_and_warn(const char *path);
+
+int import_set_nocow_and_log(int fd, const char *path);
index 91264e6306c85ca64b78aab3a62626720883fa49..48cb6aca7e8f0d027a4825b8120c510119e52862 100644 (file)
@@ -580,7 +580,7 @@ static int remove_marked_symlinks_fd(
                                 return -ENOMEM;
                         path_simplify(p, false);
 
-                        q = readlink_malloc(p, &dest);
+                        q = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &dest, NULL);
                         if (q == -ENOENT)
                                 continue;
                         if (q < 0) {
@@ -1117,7 +1117,7 @@ static int config_parse_also(
                 void *data,
                 void *userdata) {
 
-        UnitFileInstallInfo *info = userdata, *alsoinfo = NULL;
+        UnitFileInstallInfo *info = userdata;
         InstallContext *c = data;
         int r;
 
@@ -1139,7 +1139,7 @@ static int config_parse_also(
                 if (r < 0)
                         return r;
 
-                r = install_info_add(c, printed, NULL, true, &alsoinfo);
+                r = install_info_add(c, printed, NULL, true, NULL);
                 if (r < 0)
                         return r;
 
index 2f672c293543c12b3f31362b75a310589997db42..cb3762df436d7d06b61d5d2269c03129457d01a4 100644 (file)
@@ -80,6 +80,10 @@ static int access_check_var_log_journal(sd_journal *j, bool want_other_users) {
         return 1;
 }
 
+int journal_access_blocked(sd_journal *j) {
+        return hashmap_contains(j->errors, INT_TO_PTR(-EACCES));
+}
+
 int journal_access_check_and_warn(sd_journal *j, bool quiet, bool want_other_users) {
         Iterator it;
         void *code;
@@ -95,7 +99,7 @@ int journal_access_check_and_warn(sd_journal *j, bool quiet, bool want_other_use
                 return 0;
         }
 
-        if (hashmap_contains(j->errors, INT_TO_PTR(-EACCES))) {
+        if (journal_access_blocked(j)) {
                 if (!quiet)
                         (void) access_check_var_log_journal(j, want_other_users);
 
index da86434a6795e54b866c22ef7815d7b2d8c6a056..34ad1bfc8e6dafd7504c6ad7695dfe494f9bbf6f 100644 (file)
@@ -7,5 +7,5 @@
 #include "sd-journal.h"
 
 bool journal_field_valid(const char *p, size_t l, bool allow_protected);
-
+int journal_access_blocked(sd_journal *j);
 int journal_access_check_and_warn(sd_journal *j, bool quiet, bool want_other_users);
index bf158bff0d438ac673cb88152cd75fcdba1ef35e..fabff0c860e4e7216428be4b5f1e56c45195b05a 100644 (file)
@@ -26,21 +26,34 @@ assert_cc(sizeof(JsonValue) == 16U);
 /* We use fake JsonVariant objects for some special values, in order to avoid memory allocations for them. Note that
  * effectively this means that there are multiple ways to encode the same objects: via these magic values or as
  * properly allocated JsonVariant. We convert between both on-the-fly as necessary. */
-#define JSON_VARIANT_MAGIC_TRUE ((JsonVariant*) 1)
-#define JSON_VARIANT_MAGIC_FALSE ((JsonVariant*) 2)
-#define JSON_VARIANT_MAGIC_NULL ((JsonVariant*) 3)
-#define JSON_VARIANT_MAGIC_ZERO_INTEGER ((JsonVariant*) 4)
-#define JSON_VARIANT_MAGIC_ZERO_UNSIGNED ((JsonVariant*) 5)
-#define JSON_VARIANT_MAGIC_ZERO_REAL ((JsonVariant*) 6)
-#define JSON_VARIANT_MAGIC_EMPTY_STRING ((JsonVariant*) 7)
-#define JSON_VARIANT_MAGIC_EMPTY_ARRAY ((JsonVariant*) 8)
-#define JSON_VARIANT_MAGIC_EMPTY_OBJECT ((JsonVariant*) 9)
-#define _JSON_VARIANT_MAGIC_MAX ((JsonVariant*) 10)
+enum
+{
+ _JSON_VARIANT_MAGIC_TRUE = 1,
+#define JSON_VARIANT_MAGIC_TRUE ((JsonVariant*) _JSON_VARIANT_MAGIC_TRUE)
+ _JSON_VARIANT_MAGIC_FALSE,
+#define JSON_VARIANT_MAGIC_FALSE ((JsonVariant*) _JSON_VARIANT_MAGIC_FALSE)
+ _JSON_VARIANT_MAGIC_NULL,
+#define JSON_VARIANT_MAGIC_NULL ((JsonVariant*) _JSON_VARIANT_MAGIC_NULL)
+ _JSON_VARIANT_MAGIC_ZERO_INTEGER,
+#define JSON_VARIANT_MAGIC_ZERO_INTEGER ((JsonVariant*) _JSON_VARIANT_MAGIC_ZERO_INTEGER)
+ _JSON_VARIANT_MAGIC_ZERO_UNSIGNED,
+#define JSON_VARIANT_MAGIC_ZERO_UNSIGNED ((JsonVariant*) _JSON_VARIANT_MAGIC_ZERO_UNSIGNED)
+ _JSON_VARIANT_MAGIC_ZERO_REAL,
+#define JSON_VARIANT_MAGIC_ZERO_REAL ((JsonVariant*) _JSON_VARIANT_MAGIC_ZERO_REAL)
+ _JSON_VARIANT_MAGIC_EMPTY_STRING,
+#define JSON_VARIANT_MAGIC_EMPTY_STRING ((JsonVariant*) _JSON_VARIANT_MAGIC_EMPTY_STRING)
+ _JSON_VARIANT_MAGIC_EMPTY_ARRAY,
+#define JSON_VARIANT_MAGIC_EMPTY_ARRAY ((JsonVariant*) _JSON_VARIANT_MAGIC_EMPTY_ARRAY)
+ _JSON_VARIANT_MAGIC_EMPTY_OBJECT,
+#define JSON_VARIANT_MAGIC_EMPTY_OBJECT ((JsonVariant*) _JSON_VARIANT_MAGIC_EMPTY_OBJECT)
+ __JSON_VARIANT_MAGIC_MAX
+#define _JSON_VARIANT_MAGIC_MAX ((JsonVariant*) __JSON_VARIANT_MAGIC_MAX)
+};
 
 /* This is only safe as long as we don't define more than 4K magic pointers, i.e. the page size of the simplest
  * architectures we support. That's because we rely on the fact that malloc() will never allocate from the first memory
  * page, as it is a faulting page for catching NULL pointer dereferences. */
-assert_cc((uintptr_t) _JSON_VARIANT_MAGIC_MAX < 4096U);
+assert_cc((unsigned) __JSON_VARIANT_MAGIC_MAX < 4096U);
 
 enum { /* JSON tokens */
         JSON_TOKEN_END,
index 869aa279eeef008a0216272913ea5981327b16d4..e28a5df19e2e4842a33a55e47f0a0e79635efd00 100644 (file)
 #include "user-util.h"
 #include "utf8.h"
 
-/* Refuse putting together variants with a larger depth than 4K by default (as a protection against overflowing stacks
+/* Refuse putting together variants with a larger depth than 2K by default (as a protection against overflowing stacks
  * if code processes JSON objects recursively. Note that we store the depth in an uint16_t, hence make sure this
  * remains under 2^16.
- * The value was 16k, but it was discovered to be too high on llvm/x86-64. See also the issue #10738. */
-#define DEPTH_MAX (4U*1024U)
+ *
+ * The value first was 16k, but it was discovered to be too high on llvm/x86-64. See also:
+ * https://github.com/systemd/systemd/issues/10738
+ *
+ * The value then was 4k, but it was discovered to be too high on s390x/aarch64. See also:
+ * https://github.com/systemd/systemd/issues/14396 */
+
+#define DEPTH_MAX (2U*1024U)
 assert_cc(DEPTH_MAX <= UINT16_MAX);
 
 typedef struct JsonSource {
@@ -4101,7 +4107,7 @@ int json_dispatch_user_group_name(const char *name, JsonVariant *variant, JsonDi
                 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
 
         n = json_variant_string(variant);
-        if (!valid_user_group_name(n))
+        if (!valid_user_group_name_compat(n))
                 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid user/group name.", strna(name));
 
         r = free_and_strdup(s, n);
diff --git a/src/shared/libcrypt-util.c b/src/shared/libcrypt-util.c
new file mode 100644 (file)
index 0000000..f41685a
--- /dev/null
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "alloc-util.h"
+#include "libcrypt-util.h"
+#include "log.h"
+#include "macro.h"
+#include "missing_stdlib.h"
+#include "random-util.h"
+#include "string-util.h"
+#include "strv.h"
+
+int make_salt(char **ret) {
+
+#ifdef XCRYPT_VERSION_MAJOR
+        const char *e;
+        char *salt;
+
+        /* If we have libxcrypt we default to the "preferred method" (i.e. usually yescrypt), and generate it
+         * with crypt_gensalt_ra(). */
+
+        e = secure_getenv("SYSTEMD_CRYPT_PREFIX");
+        if (!e)
+                e = crypt_preferred_method();
+
+        log_debug("Generating salt for hash prefix: %s", e);
+
+        salt = crypt_gensalt_ra(e, 0, NULL, 0);
+        if (!salt)
+                return -errno;
+
+        *ret = salt;
+        return 0;
+#else
+        /* If libxcrypt is not used, we use SHA512 and generate the salt on our own since crypt_gensalt_ra()
+         * is not available. */
+
+        static const char table[] =
+                "abcdefghijklmnopqrstuvwxyz"
+                "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                "0123456789"
+                "./";
+
+        uint8_t raw[16];
+        char *salt, *j;
+        size_t i;
+        int r;
+
+        /* This is a bit like crypt_gensalt_ra(), but doesn't require libcrypt, and doesn't do anything but
+         * SHA512, i.e. is legacy-free and minimizes our deps. */
+
+        assert_cc(sizeof(table) == 64U + 1U);
+
+        /* Insist on the best randomness by setting RANDOM_BLOCK, this is about keeping passwords secret after all. */
+        r = genuine_random_bytes(raw, sizeof(raw), RANDOM_BLOCK);
+        if (r < 0)
+                return r;
+
+        salt = new(char, 3+sizeof(raw)+1+1);
+        if (!salt)
+                return -ENOMEM;
+
+        /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */
+        j = stpcpy(salt, "$6$");
+        for (i = 0; i < sizeof(raw); i++)
+                j[i] = table[raw[i] & 63];
+        j[i++] = '$';
+        j[i] = 0;
+
+        *ret = salt;
+        return 0;
+#endif
+}
+
+bool hashed_password_valid(const char *s) {
+
+        /* Returns true if the specified string is a 'valid' hashed UNIX password, i.e. if starts with '$' or
+         * with '!$' (the latter being a valid, yet locked password). */
+
+        if (isempty(s))
+                return false;
+
+        return STARTSWITH_SET(s, "$", "!$");
+}
diff --git a/src/shared/libcrypt-util.h b/src/shared/libcrypt-util.h
new file mode 100644 (file)
index 0000000..93f0e13
--- /dev/null
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if HAVE_CRYPT_H
+/* libxcrypt is a replacement for glibc's libcrypt, and libcrypt might be
+ * removed from glibc at some point. As part of the removal, defines for
+ * crypt(3) are dropped from unistd.h, and we must include crypt.h instead.
+ *
+ * Newer versions of glibc (v2.0+) already ship crypt.h with a definition
+ * of crypt(3) as well, so we simply include it if it is present.  MariaDB,
+ * MySQL, PostgreSQL, Perl and some other wide-spread packages do it the
+ * same way since ages without any problems.
+ */
+#include <crypt.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+int make_salt(char **ret);
+
+bool hashed_password_valid(const char *s);
index ce0a4acf9c894532d0373e2fd9ca88a7040b18ed..2bfd0b60c26b1198eb1802ec91ab8c52123a5b1e 100644 (file)
@@ -20,6 +20,7 @@
 #include "id128-util.h"
 #include "io-util.h"
 #include "journal-internal.h"
+#include "journal-util.h"
 #include "json.h"
 #include "log.h"
 #include "logs-show.h"
@@ -1180,71 +1181,74 @@ int show_journal(
         }
 
         for (;;) {
-                for (;;) {
-                        usec_t usec;
+                usec_t usec;
 
-                        if (need_seek) {
-                                r = sd_journal_next(j);
-                                if (r < 0)
-                                        return log_error_errno(r, "Failed to iterate through journal: %m");
-                        }
+                if (need_seek) {
+                        r = sd_journal_next(j);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to iterate through journal: %m");
+                }
 
-                        if (r == 0)
-                                break;
+                if (r == 0)
+                        break;
 
-                        need_seek = true;
+                need_seek = true;
 
-                        if (not_before > 0) {
-                                r = sd_journal_get_monotonic_usec(j, &usec, NULL);
+                if (not_before > 0) {
+                        r = sd_journal_get_monotonic_usec(j, &usec, NULL);
 
-                                /* -ESTALE is returned if the
-                                   timestamp is not from this boot */
-                                if (r == -ESTALE)
-                                        continue;
-                                else if (r < 0)
-                                        return log_error_errno(r, "Failed to get journal time: %m");
+                        /* -ESTALE is returned if the timestamp is not from this boot */
+                        if (r == -ESTALE)
+                                continue;
+                        else if (r < 0)
+                                return log_error_errno(r, "Failed to get journal time: %m");
 
-                                if (usec < not_before)
-                                        continue;
-                        }
+                        if (usec < not_before)
+                                continue;
+                }
 
-                        line++;
-                        maybe_print_begin_newline(f, &flags);
+                line++;
+                maybe_print_begin_newline(f, &flags);
 
-                        r = show_journal_entry(f, j, mode, n_columns, flags, NULL, NULL, ellipsized);
-                        if (r < 0)
-                                return r;
-                }
+                r = show_journal_entry(f, j, mode, n_columns, flags, NULL, NULL, ellipsized);
+                if (r < 0)
+                        return r;
+        }
 
-                if (warn_cutoff && line < how_many && not_before > 0) {
-                        sd_id128_t boot_id;
-                        usec_t cutoff = 0;
+        if (warn_cutoff && line < how_many && not_before > 0) {
+                sd_id128_t boot_id;
+                usec_t cutoff = 0;
 
-                        /* Check whether the cutoff line is too early */
+                /* Check whether the cutoff line is too early */
 
-                        r = sd_id128_get_boot(&boot_id);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to get boot id: %m");
+                r = sd_id128_get_boot(&boot_id);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get boot id: %m");
 
-                        r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to get journal cutoff time: %m");
+                r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get journal cutoff time: %m");
 
-                        if (r > 0 && not_before < cutoff) {
-                                maybe_print_begin_newline(f, &flags);
-                                fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
-                        }
+                if (r > 0 && not_before < cutoff) {
+                        maybe_print_begin_newline(f, &flags);
 
-                        warn_cutoff = false;
-                }
+                        /* If we logged *something* and no permission error happened, than we can reliably
+                         * emit the warning about rotation. If we didn't log anything and access errors
+                         * happened, emit hint about permissions. Otherwise, give a generic message, since we
+                         * can't diagnose the issue. */
 
-                if (!(flags & OUTPUT_FOLLOW))
-                        break;
+                        bool noaccess = journal_access_blocked(j);
 
-                r = sd_journal_wait(j, USEC_INFINITY);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to wait for journal: %m");
+                        if (line == 0 && noaccess)
+                                fprintf(f, "Warning: some journal files were not opened due to insufficient permissions.");
+                        else if (!noaccess)
+                                fprintf(f, "Warning: journal has been rotated since unit was started, output may be incomplete.\n");
+                        else
+                                fprintf(f, "Warning: journal has been rotated since unit was started and some journal "
+                                        "files were not opened due to insufficient permissions, output may be incomplete.\n");
+                }
 
+                warn_cutoff = false;
         }
 
         return 0;
@@ -1453,6 +1457,7 @@ int add_match_this_boot(sd_journal *j, const char *machine) {
 int show_journal_by_unit(
                 FILE *f,
                 const char *unit,
+                const char *log_namespace,
                 OutputMode mode,
                 unsigned n_columns,
                 usec_t not_before,
@@ -1473,7 +1478,7 @@ int show_journal_by_unit(
         if (how_many <= 0)
                 return 0;
 
-        r = sd_journal_open(&j, journal_open_flags);
+        r = sd_journal_open_namespace(&j, log_namespace, journal_open_flags | SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE);
         if (r < 0)
                 return log_error_errno(r, "Failed to open journal: %m");
 
index 1e0c4ea14646586436f9561eaf33e5fd7b7bdb2f..345efa4b2bbd091697d7bda97328ed5d5e008f4d 100644 (file)
@@ -46,6 +46,7 @@ int add_matches_for_user_unit(
 int show_journal_by_unit(
                 FILE *f,
                 const char *unit,
+                const char *namespace,
                 OutputMode mode,
                 unsigned n_columns,
                 usec_t not_before,
index 15fd514353a78f3fc9cc2deb8d51eb24413076d9..b45efcd1e6657d812b2953d76cfe41205dcbaf2f 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, &m);
+                r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
                 if (r < 0)
                         return r;
 
index 08aa480b6c8b551d465b819cdf91018a7735fc97..fa080f8e62d78765b08fe6c34a25ae18d0a0bae3 100644 (file)
@@ -27,6 +27,8 @@ shared_sources = files('''
         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
@@ -85,7 +87,14 @@ shared_sources = files('''
         fstab-util.h
         generator.c
         generator.h
+        gpt.c
         gpt.h
+        group-record-nss.c
+        group-record-nss.h
+        group-record-show.c
+        group-record-show.h
+        group-record.c
+        group-record.h
         id128-print.c
         id128-print.h
         ima-util.c
@@ -106,6 +115,8 @@ shared_sources = files('''
         json-internal.h
         json.c
         json.h
+        libcrypt-util.c
+        libcrypt-util.h
         libmount-util.h
         linux/auto_dev-ioctl.h
         linux/bpf.h
@@ -187,6 +198,14 @@ shared_sources = files('''
         uid-range.h
         unit-file.c
         unit-file.h
+        user-record-nss.c
+        user-record-nss.h
+        user-record-show.c
+        user-record-show.h
+        user-record.c
+        user-record.h
+        userdb.c
+        userdb.h
         utmp-wtmp.h
         varlink.c
         varlink.h
@@ -233,6 +252,13 @@ if conf.get('HAVE_KMOD') == 1
         shared_sources += files('module-util.c')
 endif
 
+if conf.get('HAVE_PAM') == 1
+        shared_sources += files('''
+        pam-util.c
+        pam-util.h
+'''.split())
+endif
+
 generate_ip_protocol_list = find_program('generate-ip-protocol-list.sh')
 ip_protocol_list_txt = custom_target(
         'ip-protocol-list.txt',
@@ -279,6 +305,7 @@ libshared_deps = [threads,
                   libacl,
                   libblkid,
                   libcap,
+                  libcrypt,
                   libcryptsetup,
                   libgcrypt,
                   libidn,
@@ -288,6 +315,7 @@ libshared_deps = [threads,
                   libmount,
                   libopenssl,
                   libp11kit,
+                  libpam,
                   librt,
                   libseccomp,
                   libselinux,
index 75c8ba2b781c79e260df3059d9af93724b925de0..9173cf2ffde0a65c8481b4891809773deaeaab08 100644 (file)
@@ -18,7 +18,7 @@ int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose)
         r = kmod_module_new_from_lookup(ctx, module, &modlist);
         if (r < 0)
                 return log_full_errno(verbose ? LOG_ERR : LOG_DEBUG, r,
-                                      "Failed to lookup module alias '%s': %m", module);
+                                      "Failed to look up module alias '%s': %m", module);
 
         if (!modlist) {
                 log_full_errno(verbose ? LOG_ERR : LOG_DEBUG, r,
index 8cc2d0873db2da20252a4f37d0f00c1024512792..4dbd18b86833639437f40332045e575525eeaae9 100644 (file)
@@ -65,12 +65,6 @@ int namespace_flags_to_string(unsigned long flags, char **ret) {
                         return -ENOMEM;
         }
 
-        if (!s) {
-                s = strdup("");
-                if (!s)
-                        return -ENOMEM;
-        }
-
         *ret = TAKE_PTR(s);
 
         return 0;
index 00b603205671902e5c93ec95b65f82ab1727898f..1b4765b3ec10e41faa2a0fafc55acc1151beb9c3 100644 (file)
@@ -33,15 +33,14 @@ static inline bool OUTPUT_MODE_IS_JSON(OutputMode m) {
 
 typedef enum OutputFlags {
         OUTPUT_SHOW_ALL       = 1 << 0,
-        OUTPUT_FOLLOW         = 1 << 1,
-        OUTPUT_WARN_CUTOFF    = 1 << 2,
-        OUTPUT_FULL_WIDTH     = 1 << 3,
-        OUTPUT_COLOR          = 1 << 4,
-        OUTPUT_CATALOG        = 1 << 5,
-        OUTPUT_BEGIN_NEWLINE  = 1 << 6,
-        OUTPUT_UTC            = 1 << 7,
-        OUTPUT_KERNEL_THREADS = 1 << 8,
-        OUTPUT_NO_HOSTNAME    = 1 << 9,
+        OUTPUT_WARN_CUTOFF    = 1 << 1,
+        OUTPUT_FULL_WIDTH     = 1 << 2,
+        OUTPUT_COLOR          = 1 << 3,
+        OUTPUT_CATALOG        = 1 << 4,
+        OUTPUT_BEGIN_NEWLINE  = 1 << 5,
+        OUTPUT_UTC            = 1 << 6,
+        OUTPUT_KERNEL_THREADS = 1 << 7,
+        OUTPUT_NO_HOSTNAME    = 1 << 8,
 } OutputFlags;
 
 JsonFormatFlags output_mode_to_json_format_flags(OutputMode m);
diff --git a/src/shared/pam-util.c b/src/shared/pam-util.c
new file mode 100644 (file)
index 0000000..f000798
--- /dev/null
@@ -0,0 +1,81 @@
+#include <security/pam_ext.h>
+#include <syslog.h>
+#include <stdlib.h>
+
+#include "alloc-util.h"
+#include "errno-util.h"
+#include "macro.h"
+#include "pam-util.h"
+
+int pam_log_oom(pam_handle_t *handle) {
+        /* This is like log_oom(), but uses PAM logging */
+        pam_syslog(handle, LOG_ERR, "Out of memory.");
+        return PAM_BUF_ERR;
+}
+
+int pam_bus_log_create_error(pam_handle_t *handle, int r) {
+        /* This is like bus_log_create_error(), but uses PAM logging */
+        pam_syslog(handle, LOG_ERR, "Failed to create bus message: %s", strerror_safe(r));
+        return PAM_BUF_ERR;
+}
+
+int pam_bus_log_parse_error(pam_handle_t *handle, int r) {
+        /* This is like bus_log_parse_error(), but uses PAM logging */
+        pam_syslog(handle, LOG_ERR, "Failed to parse bus message: %s", strerror_safe(r));
+        return PAM_BUF_ERR;
+}
+
+static void cleanup_system_bus(pam_handle_t *handle, void *data, int error_status) {
+        sd_bus_flush_close_unref(data);
+}
+
+int pam_acquire_bus_connection(pam_handle_t *handle, sd_bus **ret) {
+        _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+        int r;
+
+        assert(handle);
+        assert(ret);
+
+        /* We cache the bus connection so that we can share it between the session and the authentication hooks */
+        r = pam_get_data(handle, "systemd-system-bus", (const void**) &bus);
+        if (r == PAM_SUCCESS && bus) {
+                *ret = sd_bus_ref(TAKE_PTR(bus)); /* Increase the reference counter, so that the PAM data stays valid */
+                return PAM_SUCCESS;
+        }
+        if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+                pam_syslog(handle, LOG_ERR, "Failed to get bus connection: %s", pam_strerror(handle, r));
+                return r;
+        }
+
+        r = sd_bus_open_system(&bus);
+        if (r < 0) {
+                pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror_safe(r));
+                return PAM_SERVICE_ERR;
+        }
+
+        r = pam_set_data(handle, "systemd-system-bus", bus, cleanup_system_bus);
+        if (r != PAM_SUCCESS) {
+                pam_syslog(handle, LOG_ERR, "Failed to set PAM bus data: %s", pam_strerror(handle, r));
+                return r;
+        }
+
+        sd_bus_ref(bus);
+        *ret = TAKE_PTR(bus);
+
+        return PAM_SUCCESS;
+}
+
+int pam_release_bus_connection(pam_handle_t *handle) {
+        int r;
+
+        r = pam_set_data(handle, "systemd-system-bus", NULL, NULL);
+        if (r != PAM_SUCCESS)
+                pam_syslog(handle, LOG_ERR, "Failed to release PAM user record data: %s", pam_strerror(handle, r));
+
+        return r;
+}
+
+void pam_cleanup_free(pam_handle_t *handle, void *data, int error_status) {
+        /* A generic destructor for pam_set_data() that just frees the specified data */
+        free(data);
+}
diff --git a/src/shared/pam-util.h b/src/shared/pam-util.h
new file mode 100644 (file)
index 0000000..26d07b7
--- /dev/null
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <security/pam_modules.h>
+
+#include "sd-bus.h"
+
+int pam_log_oom(pam_handle_t *handle);
+int pam_bus_log_create_error(pam_handle_t *handle, int r);
+int pam_bus_log_parse_error(pam_handle_t *handle, int r);
+
+int pam_acquire_bus_connection(pam_handle_t *handle, sd_bus **ret);
+int pam_release_bus_connection(pam_handle_t *handle);
+
+void pam_cleanup_free(pam_handle_t *handle, void *data, int error_status);
index 5b162097453659ea35af3c117b82d92661420dd2..20d6c03a9ad40a4bec31f7a99501dc845d04a519 100644 (file)
@@ -479,6 +479,36 @@ static int patch_root_prefix_strv(char **l, const char *root_dir) {
         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,
@@ -496,7 +526,6 @@ int lookup_paths_init(
                 *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);
@@ -562,22 +591,9 @@ int lookup_paths_init(
                 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;
-        }
+        r = get_paths_from_environ("SYSTEMD_UNIT_PATH", &paths, &append);
+        if (r < 0)
+                return r;
 
         if (!paths || append) {
                 /* Let's figure something out. */
@@ -817,23 +833,90 @@ void lookup_paths_flush_generator(LookupPaths *p) {
 }
 
 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;
 
-        switch (scope) {
+        /* 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;
 
-        case UNIT_FILE_SYSTEM:
-                return strv_new("/run/systemd/system-generators",
-                                "/etc/systemd/system-generators",
-                                "/usr/local/lib/systemd/system-generators",
-                                SYSTEM_GENERATOR_PATH);
+        if (!paths || append) {
+                _cleanup_strv_free_ char **add = NULL;
 
-        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);
+                switch (scope) {
 
-        default:
-                assert_not_reached("Hmm, unexpected scope.");
+                case UNIT_FILE_SYSTEM:
+                        add = strv_new("/run/systemd/system-generators",
+                                       "/etc/systemd/system-generators",
+                                       "/usr/local/lib/systemd/system-generators",
+                                       SYSTEM_GENERATOR_PATH);
+                        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_PATH);
+                        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_PATH);
+                else
+                        add = strv_new("/run/systemd/user-environment-generators",
+                                       "/etc/systemd/user-environment-generators",
+                                       "/usr/local/lib/systemd/user-environment-generators",
+                                       USER_ENV_GENERATOR_PATH);
+
+                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);
 }
index f0762d248a0c65578ea31826939a61bea27de3af..b99e918144b571e059dbf74baa83447a169e5eb2 100644 (file)
@@ -72,3 +72,4 @@ void lookup_paths_flush_generator(LookupPaths *p);
 void lookup_paths_free(LookupPaths *p);
 
 char **generator_binary_paths(UnitFileScope scope);
+char **env_generator_binary_paths(bool is_system);
index 12fb3ef7ea0e2d58f7f548d65a962163cd1bbc88..8543dbd2d05f39b2bc4db7cd5bfa58d249d8dfd4 100644 (file)
@@ -9,6 +9,7 @@
 #include "fileio.h"
 #include "log.h"
 #include "macro.h"
+#include "path-util.h"
 #include "string-util.h"
 #include "sysctl-util.h"
 
@@ -16,22 +17,27 @@ char *sysctl_normalize(char *s) {
         char *n;
 
         n = strpbrk(s, "/.");
+
         /* If the first separator is a slash, the path is
          * assumed to be normalized and slashes remain slashes
          * and dots remains dots. */
-        if (!n || *n == '/')
-                return s;
-
-        /* Otherwise, dots become slashes and slashes become
-         * dots. Fun. */
-        while (n) {
-                if (*n == '.')
-                        *n = '/';
-                else
-                        *n = '.';
-
-                n = strpbrk(n + 1, "/.");
-        }
+
+        if (n && *n == '.')
+                /* Dots become slashes and slashes become dots. Fun. */
+                do {
+                        if (*n == '.')
+                                *n = '/';
+                        else
+                                *n = '.';
+
+                        n = strpbrk(n + 1, "/.");
+                } while (n);
+
+        path_simplify(s, true);
+
+        /* Kill the leading slash, but keep the first character of the string in the same place. */
+        if (*s == '/' && *(s+1))
+                memmove(s, s+1, strlen(s));
 
         return s;
 }
index cb30a9775240a5200a34371181f6045ce2aa8691..dce015151956f4627cd73f9b533dabb42a13807c 100644 (file)
@@ -6,7 +6,7 @@
 
 #include "macro.h"
 #include "stdio-util.h"
-#include "util.h"
+#include "string-util.h"
 
 char *sysctl_normalize(char *s);
 int sysctl_read(const char *property, char **value);
diff --git a/src/shared/user-record-nss.c b/src/shared/user-record-nss.c
new file mode 100644 (file)
index 0000000..0ff6d17
--- /dev/null
@@ -0,0 +1,260 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "errno-util.h"
+#include "format-util.h"
+#include "libcrypt-util.h"
+#include "strv.h"
+#include "user-record-nss.h"
+
+#define SET_IF(field, condition, value, fallback)  \
+        field = (condition) ? (value) : (fallback)
+
+int nss_passwd_to_user_record(
+                const struct passwd *pwd,
+                const struct spwd *spwd,
+                UserRecord **ret) {
+
+        _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+        int r;
+
+        assert(pwd);
+        assert(ret);
+
+        if (isempty(pwd->pw_name))
+                return -EINVAL;
+
+        if (spwd && !streq_ptr(spwd->sp_namp, pwd->pw_name))
+                return -EINVAL;
+
+        hr = user_record_new();
+        if (!hr)
+                return -ENOMEM;
+
+        r = free_and_strdup(&hr->user_name, pwd->pw_name);
+        if (r < 0)
+                return r;
+
+        r = free_and_strdup(&hr->real_name,
+                            streq_ptr(pwd->pw_gecos, hr->user_name) ? NULL : empty_to_null(pwd->pw_gecos));
+        if (r < 0)
+                return r;
+
+        r = free_and_strdup(&hr->home_directory, empty_to_null(pwd->pw_dir));
+        if (r < 0)
+                return r;
+
+        r = free_and_strdup(&hr->shell, empty_to_null(pwd->pw_shell));
+        if (r < 0)
+                return r;
+
+        hr->uid = pwd->pw_uid;
+        hr->gid = pwd->pw_gid;
+
+        if (spwd && hashed_password_valid(spwd->sp_pwdp)) {
+                strv_free_erase(hr->hashed_password);
+                hr->hashed_password = strv_new(spwd->sp_pwdp);
+                if (!hr->hashed_password)
+                        return -ENOMEM;
+        } else
+                hr->hashed_password = strv_free_erase(hr->hashed_password);
+
+        /* shadow-utils suggests using "chage -E 0" (or -E 1, depending on which man page you check)
+         * for locking a whole account, hence check for that. Note that it also defines a way to lock
+         * just a password instead of the whole account, but that's mostly pointless in times of
+         * password-less authorization, hence let's not bother. */
+
+         SET_IF(hr->locked,
+                spwd && spwd->sp_expire >= 0,
+                spwd->sp_expire <= 1, -1);
+
+         SET_IF(hr->not_after_usec,
+                spwd && spwd->sp_expire > 1 && (uint64_t) spwd->sp_expire < (UINT64_MAX-1)/USEC_PER_DAY,
+                spwd->sp_expire * USEC_PER_DAY, UINT64_MAX);
+
+         SET_IF(hr->password_change_now,
+                spwd && spwd->sp_lstchg >= 0,
+                spwd->sp_lstchg == 0, -1);
+
+         SET_IF(hr->last_password_change_usec,
+                spwd && spwd->sp_lstchg > 0 && (uint64_t) spwd->sp_lstchg <= (UINT64_MAX-1)/USEC_PER_DAY,
+                spwd->sp_lstchg * USEC_PER_DAY, UINT64_MAX);
+
+         SET_IF(hr->password_change_min_usec,
+                spwd && spwd->sp_min > 0 && (uint64_t) spwd->sp_min <= (UINT64_MAX-1)/USEC_PER_DAY,
+                spwd->sp_min * USEC_PER_DAY, UINT64_MAX);
+
+         SET_IF(hr->password_change_max_usec,
+                spwd && spwd->sp_max > 0 && (uint64_t) spwd->sp_max <= (UINT64_MAX-1)/USEC_PER_DAY,
+                spwd->sp_max * USEC_PER_DAY, UINT64_MAX);
+
+         SET_IF(hr->password_change_warn_usec,
+                spwd && spwd->sp_warn > 0 && (uint64_t) spwd->sp_warn <= (UINT64_MAX-1)/USEC_PER_DAY,
+                spwd->sp_warn * USEC_PER_DAY, UINT64_MAX);
+
+         SET_IF(hr->password_change_inactive_usec,
+                spwd && spwd->sp_inact > 0 && (uint64_t) spwd->sp_inact <= (UINT64_MAX-1)/USEC_PER_DAY,
+                spwd->sp_inact * USEC_PER_DAY, UINT64_MAX);
+
+        hr->json = json_variant_unref(hr->json);
+        r = json_build(&hr->json, JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(hr->user_name)),
+                                       JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(hr->uid)),
+                                       JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(hr->gid)),
+                                       JSON_BUILD_PAIR_CONDITION(hr->real_name, "realName", JSON_BUILD_STRING(hr->real_name)),
+                                       JSON_BUILD_PAIR_CONDITION(hr->home_directory, "homeDirectory", JSON_BUILD_STRING(hr->home_directory)),
+                                       JSON_BUILD_PAIR_CONDITION(hr->shell, "shell", JSON_BUILD_STRING(hr->shell)),
+                                       JSON_BUILD_PAIR_CONDITION(!strv_isempty(hr->hashed_password), "privileged", JSON_BUILD_OBJECT(JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRV(hr->hashed_password)))),
+                                       JSON_BUILD_PAIR_CONDITION(hr->locked >= 0, "locked", JSON_BUILD_BOOLEAN(hr->locked)),
+                                       JSON_BUILD_PAIR_CONDITION(hr->not_after_usec != UINT64_MAX, "notAfterUSec", JSON_BUILD_UNSIGNED(hr->not_after_usec)),
+                                       JSON_BUILD_PAIR_CONDITION(hr->password_change_now >= 0, "passwordChangeNow", JSON_BUILD_BOOLEAN(hr->password_change_now)),
+                                       JSON_BUILD_PAIR_CONDITION(hr->last_password_change_usec != UINT64_MAX, "lastPasswordChangeUSec", JSON_BUILD_UNSIGNED(hr->last_password_change_usec)),
+                                       JSON_BUILD_PAIR_CONDITION(hr->password_change_min_usec != UINT64_MAX, "passwordChangeMinUSec", JSON_BUILD_UNSIGNED(hr->password_change_min_usec)),
+                                       JSON_BUILD_PAIR_CONDITION(hr->password_change_max_usec != UINT64_MAX, "passwordChangeMaxUSec", JSON_BUILD_UNSIGNED(hr->password_change_max_usec)),
+                                       JSON_BUILD_PAIR_CONDITION(hr->password_change_warn_usec != UINT64_MAX, "passwordChangeWarnUSec", JSON_BUILD_UNSIGNED(hr->password_change_warn_usec)),
+                                       JSON_BUILD_PAIR_CONDITION(hr->password_change_inactive_usec != UINT64_MAX, "passwordChangeInactiveUSec", JSON_BUILD_UNSIGNED(hr->password_change_inactive_usec))));
+
+        if (r < 0)
+                return r;
+
+        hr->mask = USER_RECORD_REGULAR |
+                (!strv_isempty(hr->hashed_password) ? USER_RECORD_PRIVILEGED : 0);
+
+        *ret = TAKE_PTR(hr);
+        return 0;
+}
+
+int nss_spwd_for_passwd(const struct passwd *pwd, struct spwd *ret_spwd, char **ret_buffer) {
+        size_t buflen = 4096;
+        int r;
+
+        assert(pwd);
+        assert(ret_spwd);
+        assert(ret_buffer);
+
+        for (;;) {
+                _cleanup_free_ char *buf = NULL;
+                struct spwd spwd, *result;
+
+                buf = malloc(buflen);
+                if (!buf)
+                        return -ENOMEM;
+
+                r = getspnam_r(pwd->pw_name, &spwd, buf, buflen, &result);
+                if (r == 0) {
+                        if (!result)
+                                return -ESRCH;
+
+                        *ret_spwd = *result;
+                        *ret_buffer = TAKE_PTR(buf);
+                        return 0;
+                }
+                if (r < 0)
+                        return -EIO; /* Weird, this should not return negative! */
+                if (r != ERANGE)
+                        return -r;
+
+                if (buflen > SIZE_MAX / 2)
+                        return -ERANGE;
+
+                buflen *= 2;
+                buf = mfree(buf);
+        }
+}
+
+int nss_user_record_by_name(const char *name, UserRecord **ret) {
+        _cleanup_free_ char *buf = NULL, *sbuf = NULL;
+        struct passwd pwd, *result;
+        bool incomplete = false;
+        size_t buflen = 4096;
+        struct spwd spwd;
+        int r;
+
+        assert(name);
+        assert(ret);
+
+        for (;;) {
+                buf = malloc(buflen);
+                if (!buf)
+                        return -ENOMEM;
+
+                r = getpwnam_r(name, &pwd, buf, buflen, &result);
+                if (r == 0)  {
+                        if (!result)
+                                return -ESRCH;
+
+                        break;
+                }
+
+                if (r < 0)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EIO), "getpwnam_r() returned a negative value");
+                if (r != ERANGE)
+                        return -r;
+
+                if (buflen > SIZE_MAX / 2)
+                        return -ERANGE;
+
+                buflen *= 2;
+                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);
+        }
+
+        r = nss_passwd_to_user_record(result, r >= 0 ? &spwd : NULL, ret);
+        if (r < 0)
+                return r;
+
+        (*ret)->incomplete = incomplete;
+        return 0;
+}
+
+int nss_user_record_by_uid(uid_t uid, UserRecord **ret) {
+        _cleanup_free_ char *buf = NULL, *sbuf = NULL;
+        struct passwd pwd, *result;
+        bool incomplete = false;
+        size_t buflen = 4096;
+        struct spwd spwd;
+        int r;
+
+        assert(ret);
+
+        for (;;) {
+                buf = malloc(buflen);
+                if (!buf)
+                        return -ENOMEM;
+
+                r = getpwuid_r(uid, &pwd, buf, buflen, &result);
+                if (r == 0)  {
+                        if (!result)
+                                return -ESRCH;
+
+                        break;
+                }
+                if (r < 0)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EIO), "getpwuid_r() returned a negative value");
+                if (r != ERANGE)
+                        return -r;
+
+                if (buflen > SIZE_MAX / 2)
+                        return -ERANGE;
+
+                buflen *= 2;
+                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);
+        }
+
+        r = nss_passwd_to_user_record(result, r >= 0 ? &spwd : NULL, ret);
+        if (r < 0)
+                return r;
+
+        (*ret)->incomplete = incomplete;
+        return 0;
+}
diff --git a/src/shared/user-record-nss.h b/src/shared/user-record-nss.h
new file mode 100644 (file)
index 0000000..d5fb23a
--- /dev/null
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <pwd.h>
+#include <shadow.h>
+
+#include "user-record.h"
+
+/* Synthesizes a UserRecord object from NSS data */
+
+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);
diff --git a/src/shared/user-record-show.c b/src/shared/user-record-show.c
new file mode 100644 (file)
index 0000000..e5eff50
--- /dev/null
@@ -0,0 +1,466 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "format-util.h"
+#include "fs-util.h"
+#include "group-record.h"
+#include "process-util.h"
+#include "rlimit-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "user-record-show.h"
+#include "user-util.h"
+#include "userdb.h"
+
+const char *user_record_state_color(const char *state) {
+        if (STR_IN_SET(state, "unfixated", "absent"))
+                return ansi_grey();
+        else if (streq(state, "active"))
+                return ansi_highlight_green();
+        else if (streq(state, "locked"))
+                 return ansi_highlight_yellow();
+
+        return NULL;
+}
+
+void user_record_show(UserRecord *hr, bool show_full_group_info) {
+        const char *hd, *ip, *shell;
+        UserStorage storage;
+        usec_t t;
+        size_t k;
+        int r, b;
+
+        printf("   User name: %s\n",
+               user_record_user_name_and_realm(hr));
+
+        if (hr->state) {
+                const char *color;
+
+                color = user_record_state_color(hr->state);
+
+                printf("       State: %s%s%s\n",
+                       strempty(color), hr->state, color ? ansi_normal() : "");
+        }
+
+        printf(" Disposition: %s\n", user_disposition_to_string(user_record_disposition(hr)));
+
+        if (hr->last_change_usec != USEC_INFINITY) {
+                char buf[FORMAT_TIMESTAMP_MAX];
+                printf(" Last Change: %s\n", format_timestamp(buf, sizeof(buf), hr->last_change_usec));
+        }
+
+        if (hr->last_password_change_usec != USEC_INFINITY &&
+            hr->last_password_change_usec != hr->last_change_usec) {
+                char buf[FORMAT_TIMESTAMP_MAX];
+                printf(" Last Passw.: %s\n", format_timestamp(buf, sizeof(buf), hr->last_password_change_usec));
+        }
+
+        r = user_record_test_blocked(hr);
+        switch (r) {
+
+        case -ESTALE:
+                printf("    Login OK: %sno%s (last change time is in the future)\n", ansi_highlight_red(), ansi_normal());
+                break;
+
+        case -ENOLCK:
+                printf("    Login OK: %sno%s (record is locked)\n", ansi_highlight_red(), ansi_normal());
+                break;
+
+        case -EL2HLT:
+                printf("    Login OK: %sno%s (record not valid yet))\n", ansi_highlight_red(), ansi_normal());
+                break;
+
+        case -EL3HLT:
+                printf("    Login OK: %sno%s (record not valid anymore))\n", ansi_highlight_red(), ansi_normal());
+                break;
+
+        default: {
+                usec_t y;
+
+                if (r < 0) {
+                        errno = -r;
+                        printf("    Login OK: %sno%s (%m)\n", ansi_highlight_red(), ansi_normal());
+                        break;
+                }
+
+                if (is_nologin_shell(user_record_shell(hr))) {
+                        printf("    Login OK: %sno%s (nologin shell)\n", ansi_highlight_red(), ansi_normal());
+                        break;
+                }
+
+                y = user_record_ratelimit_next_try(hr);
+                if (y != USEC_INFINITY && y > now(CLOCK_REALTIME)) {
+                        printf("    Login OK: %sno%s (ratelimit)\n", ansi_highlight_red(), ansi_normal());
+                        break;
+                }
+
+                printf("    Login OK: %syes%s\n", ansi_highlight_green(), ansi_normal());
+                break;
+        }}
+
+        r = user_record_test_password_change_required(hr);
+        switch (r) {
+
+        case -EKEYREVOKED:
+                printf(" Password OK: %schange now%s\n", ansi_highlight_yellow(), ansi_normal());
+                break;
+
+        case -EOWNERDEAD:
+                printf(" Password OK: %sexpired%s (change now!)\n", ansi_highlight_yellow(), ansi_normal());
+                break;
+
+        case -EKEYREJECTED:
+                printf(" Password OK: %sexpired%s (for good)\n", ansi_highlight_red(), ansi_normal());
+                break;
+
+        case -EKEYEXPIRED:
+                printf(" Password OK: %sexpires soon%s\n", ansi_highlight_yellow(), ansi_normal());
+                break;
+
+        case -ENETDOWN:
+                printf(" Password OK: %sno timestamp%s\n", ansi_highlight_red(), ansi_normal());
+                break;
+
+        case -EROFS:
+                printf(" Password OK: %schange not permitted%s\n", ansi_highlight_yellow(), ansi_normal());
+                break;
+
+        default:
+                if (r < 0) {
+                        errno = -r;
+                        printf(" Password OK: %sno%s (%m)\n", ansi_highlight_yellow(), ansi_normal());
+                        break;
+                }
+
+                printf(" Password OK: %syes%s\n", ansi_highlight_green(), ansi_normal());
+                break;
+        }
+
+        if (uid_is_valid(hr->uid))
+                printf("         UID: " UID_FMT "\n", hr->uid);
+        if (gid_is_valid(hr->gid)) {
+                if (show_full_group_info) {
+                        _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
+
+                        r = groupdb_by_gid(hr->gid, 0, &gr);
+                        if (r < 0) {
+                                errno = -r;
+                                printf("         GID: " GID_FMT " (unresolvable: %m)\n", hr->gid);
+                        } else
+                                printf("         GID: " GID_FMT " (%s)\n", hr->gid, gr->group_name);
+                } else
+                        printf("         GID: " GID_FMT "\n", hr->gid);
+        } else if (uid_is_valid(hr->uid)) /* Show UID as GID if not separately configured */
+                printf("         GID: " GID_FMT "\n", (gid_t) hr->uid);
+
+        if (show_full_group_info) {
+                _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+
+                r = membershipdb_by_user(hr->user_name, 0, &iterator);
+                if (r < 0) {
+                        errno = -r;
+                        printf(" Aux. Groups: (can't acquire: %m)\n");
+                } else {
+                        const char *prefix = " Aux. Groups:";
+
+                        for (;;) {
+                                _cleanup_free_ char *group = NULL;
+
+                                r = membershipdb_iterator_get(iterator, NULL, &group);
+                                if (r == -ESRCH)
+                                        break;
+                                if (r < 0) {
+                                        errno = -r;
+                                        printf("%s (can't iterate: %m)\n", prefix);
+                                        break;
+                                }
+
+                                printf("%s %s\n", prefix, group);
+                                prefix = "             ";
+                        }
+                }
+        }
+
+        if (hr->real_name && !streq(hr->real_name, hr->user_name))
+                printf("   Real Name: %s\n", hr->real_name);
+
+        hd = user_record_home_directory(hr);
+        if (hd)
+                printf("   Directory: %s\n", hd);
+
+        storage = user_record_storage(hr);
+        if (storage >= 0) /* Let's be political, and clarify which storage we like, and which we don't. About CIFS we don't complain. */
+                printf("     Storage: %s%s\n", user_storage_to_string(storage),
+                       storage == USER_LUKS ? " (strong encryption)" :
+                       storage == USER_FSCRYPT ? " (weak encryption)" :
+                       IN_SET(storage, USER_DIRECTORY, USER_SUBVOLUME) ? " (no encryption)" : "");
+
+        ip = user_record_image_path(hr);
+        if (ip && !streq_ptr(ip, hd))
+                printf("  Image Path: %s\n", ip);
+
+        b = user_record_removable(hr);
+        if (b >= 0)
+                printf("   Removable: %s\n", yes_no(b));
+
+        shell = user_record_shell(hr);
+        if (shell)
+                printf("       Shell: %s\n", shell);
+
+        if (hr->email_address)
+                printf("       Email: %s\n", hr->email_address);
+        if (hr->location)
+                printf("    Location: %s\n", hr->location);
+        if (hr->password_hint)
+                printf(" Passw. Hint: %s\n", hr->password_hint);
+        if (hr->icon_name)
+                printf("   Icon Name: %s\n", hr->icon_name);
+
+        if (hr->time_zone)
+                printf("   Time Zone: %s\n", hr->time_zone);
+
+        if (hr->preferred_language)
+                printf("    Language: %s\n", hr->preferred_language);
+
+        if (!strv_isempty(hr->environment)) {
+                char **i;
+
+                STRV_FOREACH(i, hr->environment) {
+                        printf(i == hr->environment ?
+                               " Environment: %s\n" :
+                               "              %s\n", *i);
+                }
+        }
+
+        if (hr->locked >= 0)
+                printf("      Locked: %s\n", yes_no(hr->locked));
+
+        if (hr->not_before_usec != UINT64_MAX) {
+                char buf[FORMAT_TIMESTAMP_MAX];
+                printf("  Not Before: %s\n", format_timestamp(buf, sizeof(buf), hr->not_before_usec));
+        }
+
+        if (hr->not_after_usec != UINT64_MAX) {
+                char buf[FORMAT_TIMESTAMP_MAX];
+                printf("   Not After: %s\n", format_timestamp(buf, sizeof(buf), hr->not_after_usec));
+        }
+
+        if (hr->umask != MODE_INVALID)
+                printf("       UMask: 0%03o\n", hr->umask);
+
+        if (nice_is_valid(hr->nice_level))
+                printf("        Nice: %i\n", hr->nice_level);
+
+        for (int j = 0; j < _RLIMIT_MAX; j++) {
+                if (hr->rlimits[j])
+                        printf("       Limit: RLIMIT_%s=%" PRIu64 ":%" PRIu64 "\n",
+                               rlimit_to_string(j), (uint64_t) hr->rlimits[j]->rlim_cur, (uint64_t) hr->rlimits[j]->rlim_max);
+        }
+
+        if (hr->tasks_max != UINT64_MAX)
+                printf("   Tasks Max: %" PRIu64 "\n", hr->tasks_max);
+
+        if (hr->memory_high != UINT64_MAX) {
+                char buf[FORMAT_BYTES_MAX];
+                printf(" Memory High: %s\n", format_bytes(buf, sizeof(buf), hr->memory_high));
+        }
+
+        if (hr->memory_max != UINT64_MAX) {
+                char buf[FORMAT_BYTES_MAX];
+                printf("  Memory Max: %s\n", format_bytes(buf, sizeof(buf), hr->memory_max));
+        }
+
+        if (hr->cpu_weight != UINT64_MAX)
+                printf("  CPU Weight: %" PRIu64 "\n", hr->cpu_weight);
+
+        if (hr->io_weight != UINT64_MAX)
+                printf("   IO Weight: %" PRIu64 "\n", hr->io_weight);
+
+        if (hr->access_mode != MODE_INVALID)
+                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)));
+
+                if (!sd_id128_is_null(hr->luks_uuid))
+                        printf("   LUKS UUID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(hr->luks_uuid));
+                if (!sd_id128_is_null(hr->partition_uuid))
+                        printf("   Part UUID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(hr->partition_uuid));
+                if (!sd_id128_is_null(hr->file_system_uuid))
+                        printf("     FS UUID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(hr->file_system_uuid));
+
+                if (hr->file_system_type)
+                        printf(" File System: %s\n", user_record_file_system_type(hr));
+
+                if (hr->luks_cipher)
+                        printf(" LUKS Cipher: %s\n", hr->luks_cipher);
+                if (hr->luks_cipher_mode)
+                        printf(" Cipher Mode: %s\n", hr->luks_cipher_mode);
+                if (hr->luks_volume_key_size != UINT64_MAX)
+                        printf("  Volume Key: %" PRIu64 "bit\n", hr->luks_volume_key_size * 8);
+
+                if (hr->luks_pbkdf_type)
+                        printf("  PBKDF Type: %s\n", hr->luks_pbkdf_type);
+                if (hr->luks_pbkdf_hash_algorithm)
+                        printf("  PBKDF Hash: %s\n", hr->luks_pbkdf_hash_algorithm);
+                if (hr->luks_pbkdf_time_cost_usec != UINT64_MAX) {
+                        char buf[FORMAT_TIMESPAN_MAX];
+                        printf("  PBKDF Time: %s\n", format_timespan(buf, sizeof(buf), hr->luks_pbkdf_time_cost_usec, 0));
+                }
+                if (hr->luks_pbkdf_memory_cost != UINT64_MAX) {
+                        char buf[FORMAT_BYTES_MAX];
+                        printf(" PBKDF Bytes: %s\n", format_bytes(buf, sizeof(buf), hr->luks_pbkdf_memory_cost));
+                }
+                if (hr->luks_pbkdf_parallel_threads != UINT64_MAX)
+                        printf("PBKDF Thread: %" PRIu64 "\n", hr->luks_pbkdf_parallel_threads);
+
+        } else if (storage == USER_CIFS) {
+
+                if (hr->cifs_service)
+                        printf("CIFS Service: %s\n", hr->cifs_service);
+        }
+
+        if (hr->cifs_user_name)
+                printf("   CIFS User: %s\n", user_record_cifs_user_name(hr));
+        if (hr->cifs_domain)
+                printf(" CIFS Domain: %s\n", hr->cifs_domain);
+
+        if (storage != USER_CLASSIC)
+                printf(" Mount Flags: %s %s %s\n",
+                       hr->nosuid ? "nosuid" : "suid",
+                       hr->nodev ? "nodev" : "dev",
+                       hr->noexec ? "noexec" : "exec");
+
+        if (hr->skeleton_directory)
+                printf("  Skel. Dir.: %s\n", user_record_skeleton_directory(hr));
+
+        if (hr->disk_size != UINT64_MAX) {
+                char buf[FORMAT_BYTES_MAX];
+                printf("   Disk Size: %s\n", format_bytes(buf, sizeof(buf), hr->disk_size));
+        }
+
+        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_free != UINT64_MAX) {
+                char buf[FORMAT_BYTES_MAX];
+                printf("   Disk Free: %s\n", format_bytes(buf, sizeof(buf), hr->disk_free));
+        }
+
+        if (hr->disk_floor != UINT64_MAX) {
+                char buf[FORMAT_BYTES_MAX];
+                printf("  Disk Floor: %s\n", format_bytes(buf, sizeof(buf), hr->disk_floor));
+        }
+
+        if (hr->disk_ceiling != UINT64_MAX) {
+                char buf[FORMAT_BYTES_MAX];
+                printf("Disk Ceiling: %s\n", format_bytes(buf, sizeof(buf), hr->disk_ceiling));
+        }
+
+        if (hr->good_authentication_counter != UINT64_MAX)
+                printf("  Good Auth.: %" PRIu64 "\n", hr->good_authentication_counter);
+
+        if (hr->last_good_authentication_usec != UINT64_MAX) {
+                char buf[FORMAT_TIMESTAMP_MAX];
+                printf("   Last Good: %s\n", format_timestamp(buf, sizeof(buf), hr->last_good_authentication_usec));
+        }
+
+        if (hr->bad_authentication_counter != UINT64_MAX)
+                printf("   Bad Auth.: %" PRIu64 "\n", hr->bad_authentication_counter);
+
+        if (hr->last_bad_authentication_usec != UINT64_MAX) {
+                char buf[FORMAT_TIMESTAMP_MAX];
+                printf("    Last Bad: %s\n", format_timestamp(buf, sizeof(buf), hr->last_bad_authentication_usec));
+        }
+
+        t = user_record_ratelimit_next_try(hr);
+        if (t != USEC_INFINITY) {
+                usec_t n = now(CLOCK_REALTIME);
+
+                if (t <= n)
+                        printf("    Next Try: anytime\n");
+                else {
+                        char buf[FORMAT_TIMESPAN_MAX];
+                        printf("    Next Try: %sin %s%s\n",
+                               ansi_highlight_red(),
+                               format_timespan(buf, sizeof(buf), t - n, USEC_PER_SEC),
+                               ansi_normal());
+                }
+        }
+
+        if (storage != USER_CLASSIC) {
+                char buf[FORMAT_TIMESPAN_MAX];
+                printf(" Auth. Limit: %" PRIu64 " attempts per %s\n", user_record_ratelimit_burst(hr),
+                       format_timespan(buf, sizeof(buf), user_record_ratelimit_interval_usec(hr), 0));
+        }
+
+        if (hr->enforce_password_policy >= 0)
+                printf(" Passwd Pol.: %s\n", yes_no(hr->enforce_password_policy));
+
+        if (hr->password_change_min_usec != UINT64_MAX ||
+            hr->password_change_max_usec != UINT64_MAX ||
+            hr->password_change_warn_usec != UINT64_MAX ||
+            hr->password_change_inactive_usec != UINT64_MAX) {
+
+                char buf[FORMAT_TIMESPAN_MAX];
+                printf(" Passwd Chg.:");
+
+                if (hr->password_change_min_usec != UINT64_MAX) {
+                        printf(" min %s", format_timespan(buf, sizeof(buf), hr->password_change_min_usec, 0));
+
+                        if (hr->password_change_max_usec != UINT64_MAX)
+                                printf(" …");
+                }
+
+                if (hr->password_change_max_usec != UINT64_MAX)
+                        printf(" max %s", format_timespan(buf, sizeof(buf), hr->password_change_max_usec, 0));
+
+                if (hr->password_change_warn_usec != UINT64_MAX)
+                        printf("/warn %s", format_timespan(buf, sizeof(buf), hr->password_change_warn_usec, 0));
+
+                if (hr->password_change_inactive_usec != UINT64_MAX)
+                        printf("/inactive %s", format_timespan(buf, sizeof(buf), hr->password_change_inactive_usec, 0));
+
+                printf("\n");
+        }
+
+        if (hr->password_change_now >= 0)
+                printf("Pas. Ch. Now: %s\n", yes_no(hr->password_change_now));
+
+        if (!strv_isempty(hr->ssh_authorized_keys))
+                printf("SSH Pub. Key: %zu\n", strv_length(hr->ssh_authorized_keys));
+
+        if (!strv_isempty(hr->pkcs11_token_uri)) {
+                char **i;
+
+                STRV_FOREACH(i, hr->pkcs11_token_uri)
+                        printf(i == hr->pkcs11_token_uri ?
+                               "  Sec. Token: %s\n" :
+                               "              %s\n", *i);
+        }
+
+        k = strv_length(hr->hashed_password);
+        if (k == 0)
+                printf("   Passwords: %snone%s\n",
+                       user_record_disposition(hr) == USER_REGULAR ? ansi_highlight_yellow() : ansi_normal(), ansi_normal());
+        else
+                printf("   Passwords: %zu\n", k);
+
+        if (hr->signed_locally >= 0)
+                printf("  Local Sig.: %s\n", yes_no(hr->signed_locally));
+
+        if (hr->stop_delay_usec != UINT64_MAX) {
+                char buf[FORMAT_TIMESPAN_MAX];
+                printf("  Stop Delay: %s\n", format_timespan(buf, sizeof(buf), hr->stop_delay_usec, 0));
+        }
+
+        if (hr->auto_login >= 0)
+                printf("Autom. Login: %s\n", yes_no(hr->auto_login));
+
+        if (hr->kill_processes >= 0)
+                printf("  Kill Proc.: %s\n", yes_no(hr->kill_processes));
+
+        if (hr->service)
+                printf("     Service: %s\n", hr->service);
+}
diff --git a/src/shared/user-record-show.h b/src/shared/user-record-show.h
new file mode 100644 (file)
index 0000000..bd22be2
--- /dev/null
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "user-record.h"
+
+const char *user_record_state_color(const char *state);
+
+void user_record_show(UserRecord *hr, bool show_full_group_info);
diff --git a/src/shared/user-record.c b/src/shared/user-record.c
new file mode 100644 (file)
index 0000000..ff2cc41
--- /dev/null
@@ -0,0 +1,1883 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/mount.h>
+
+#include "cgroup-util.h"
+#include "dns-domain.h"
+#include "env-util.h"
+#include "fs-util.h"
+#include "hexdecoct.h"
+#include "hostname-util.h"
+#include "memory-util.h"
+#include "path-util.h"
+#include "pkcs11-util.h"
+#include "rlimit-util.h"
+#include "stat-util.h"
+#include "string-table.h"
+#include "strv.h"
+#include "user-record.h"
+#include "user-util.h"
+
+#define DEFAULT_RATELIMIT_BURST 30
+#define DEFAULT_RATELIMIT_INTERVAL_USEC (1*USEC_PER_MINUTE)
+
+UserRecord* user_record_new(void) {
+        UserRecord *h;
+
+        h = new(UserRecord, 1);
+        if (!h)
+                return NULL;
+
+        *h = (UserRecord) {
+                .n_ref = 1,
+                .disposition = _USER_DISPOSITION_INVALID,
+                .last_change_usec = UINT64_MAX,
+                .last_password_change_usec = UINT64_MAX,
+                .umask = MODE_INVALID,
+                .nice_level = INT_MAX,
+                .not_before_usec = UINT64_MAX,
+                .not_after_usec = UINT64_MAX,
+                .locked = -1,
+                .storage = _USER_STORAGE_INVALID,
+                .access_mode = MODE_INVALID,
+                .disk_size = UINT64_MAX,
+                .disk_size_relative = UINT64_MAX,
+                .tasks_max = UINT64_MAX,
+                .memory_high = UINT64_MAX,
+                .memory_max = UINT64_MAX,
+                .cpu_weight = UINT64_MAX,
+                .io_weight = UINT64_MAX,
+                .uid = UID_INVALID,
+                .gid = GID_INVALID,
+                .nodev = true,
+                .nosuid = true,
+                .luks_discard = -1,
+                .luks_volume_key_size = UINT64_MAX,
+                .luks_pbkdf_time_cost_usec = UINT64_MAX,
+                .luks_pbkdf_memory_cost = UINT64_MAX,
+                .luks_pbkdf_parallel_threads = UINT64_MAX,
+                .disk_usage = UINT64_MAX,
+                .disk_free = UINT64_MAX,
+                .disk_ceiling = UINT64_MAX,
+                .disk_floor = UINT64_MAX,
+                .signed_locally = -1,
+                .good_authentication_counter = UINT64_MAX,
+                .bad_authentication_counter = UINT64_MAX,
+                .last_good_authentication_usec = UINT64_MAX,
+                .last_bad_authentication_usec = UINT64_MAX,
+                .ratelimit_begin_usec = UINT64_MAX,
+                .ratelimit_count = UINT64_MAX,
+                .ratelimit_interval_usec = UINT64_MAX,
+                .ratelimit_burst = UINT64_MAX,
+                .removable = -1,
+                .enforce_password_policy = -1,
+                .auto_login = -1,
+                .stop_delay_usec = UINT64_MAX,
+                .kill_processes = -1,
+                .password_change_min_usec = UINT64_MAX,
+                .password_change_max_usec = UINT64_MAX,
+                .password_change_warn_usec = UINT64_MAX,
+                .password_change_inactive_usec = UINT64_MAX,
+                .password_change_now = -1,
+                .pkcs11_protected_authentication_path_permitted = -1,
+        };
+
+        return h;
+}
+
+static void pkcs11_encrypted_key_done(Pkcs11EncryptedKey *k) {
+        if (!k)
+                return;
+
+        free(k->uri);
+        erase_and_free(k->data);
+        erase_and_free(k->hashed_password);
+}
+
+static UserRecord* user_record_free(UserRecord *h) {
+        if (!h)
+                return NULL;
+
+        free(h->user_name);
+        free(h->realm);
+        free(h->user_name_and_realm_auto);
+        free(h->real_name);
+        free(h->email_address);
+        erase_and_free(h->password_hint);
+        free(h->location);
+        free(h->icon_name);
+
+        free(h->shell);
+
+        strv_free(h->environment);
+        free(h->time_zone);
+        free(h->preferred_language);
+        rlimit_free_all(h->rlimits);
+
+        free(h->skeleton_directory);
+
+        strv_free_erase(h->hashed_password);
+        strv_free_erase(h->ssh_authorized_keys);
+        strv_free_erase(h->password);
+        strv_free_erase(h->pkcs11_pin);
+
+        free(h->cifs_service);
+        free(h->cifs_user_name);
+        free(h->cifs_domain);
+
+        free(h->image_path);
+        free(h->image_path_auto);
+        free(h->home_directory);
+        free(h->home_directory_auto);
+
+        strv_free(h->member_of);
+
+        free(h->file_system_type);
+        free(h->luks_cipher);
+        free(h->luks_cipher_mode);
+        free(h->luks_pbkdf_hash_algorithm);
+        free(h->luks_pbkdf_type);
+
+        free(h->state);
+        free(h->service);
+
+        strv_free(h->pkcs11_token_uri);
+        for (size_t i = 0; i < h->n_pkcs11_encrypted_key; i++)
+                pkcs11_encrypted_key_done(h->pkcs11_encrypted_key + i);
+        free(h->pkcs11_encrypted_key);
+
+        json_variant_unref(h->json);
+
+        return mfree(h);
+}
+
+DEFINE_TRIVIAL_REF_UNREF_FUNC(UserRecord, user_record, user_record_free);
+
+int json_dispatch_realm(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        char **s = userdata;
+        const char *n;
+        int r;
+
+        if (json_variant_is_null(variant)) {
+                *s = mfree(*s);
+                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));
+
+        n = json_variant_string(variant);
+        r = dns_name_is_valid(n);
+        if (r < 0)
+                return json_log(variant, flags, r, "Failed to check if JSON field '%s' is a valid DNS domain.", strna(name));
+        if (r == 0)
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid DNS domain.", strna(name));
+
+        r = free_and_strdup(s, n);
+        if (r < 0)
+                return json_log(variant, flags, r, "Failed to allocate string: %m");
+
+        return 0;
+}
+
+static int json_dispatch_gecos(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        char **s = userdata;
+        const char *n;
+        int r;
+
+        if (json_variant_is_null(variant)) {
+                *s = mfree(*s);
+                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));
+
+        n = json_variant_string(variant);
+        if (!valid_gecos(n))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid GECOS compatible real name.", strna(name));
+
+        r = free_and_strdup(s, n);
+        if (r < 0)
+                return json_log(variant, flags, r, "Failed to allocate string: %m");
+
+        return 0;
+}
+
+static int json_dispatch_nice(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        int *nl = userdata;
+        intmax_t m;
+
+        if (json_variant_is_null(variant)) {
+                *nl = INT_MAX;
+                return 0;
+        }
+
+        if (!json_variant_is_integer(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
+
+        m = json_variant_integer(variant);
+        if (m < PRIO_MIN || m >= PRIO_MAX)
+                return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' is not a valid nice level.", strna(name));
+
+        *nl = m;
+        return 0;
+}
+
+static int json_dispatch_rlimit_value(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        rlim_t *ret = userdata;
+
+        if (json_variant_is_null(variant))
+                *ret = RLIM_INFINITY;
+        else if (json_variant_is_unsigned(variant)) {
+                uintmax_t w;
+
+                w = json_variant_unsigned(variant);
+                if (w == RLIM_INFINITY || (uintmax_t) w != json_variant_unsigned(variant))
+                        return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "Resource limit value '%s' is out of range.", name);
+
+                *ret = (rlim_t) w;
+        } else
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit value '%s' is not an unsigned integer.", name);
+
+        return 0;
+}
+
+static int json_dispatch_rlimits(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        struct rlimit** limits = userdata;
+        JsonVariant *value;
+        const char *key;
+        int r;
+
+        assert_se(limits);
+
+        if (json_variant_is_null(variant)) {
+                rlimit_free_all(limits);
+                return 0;
+        }
+
+        if (!json_variant_is_object(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
+
+        JSON_VARIANT_OBJECT_FOREACH(key, value, variant) {
+                JsonVariant *jcur, *jmax;
+                struct rlimit rl;
+                const char *p;
+                int l;
+
+                p = startswith(key, "RLIMIT_");
+                if (!p)
+                        l = -1;
+                else
+                        l = rlimit_from_string(p);
+                if (l < 0)
+                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit '%s' not known.", key);
+
+                if (!json_variant_is_object(value))
+                        return json_log(value, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit '%s' has invalid value.", key);
+
+                if (json_variant_elements(value) != 4)
+                        return json_log(value, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit '%s' value is does not have two fields as expected.", key);
+
+                jcur = json_variant_by_key(value, "cur");
+                if (!jcur)
+                        return json_log(value, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit '%s' lacks 'cur' field.", key);
+                r = json_dispatch_rlimit_value("cur", jcur, flags, &rl.rlim_cur);
+                if (r < 0)
+                        return r;
+
+                jmax = json_variant_by_key(value, "max");
+                if (!jmax)
+                        return json_log(value, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit '%s' lacks 'max' field.", key);
+                r = json_dispatch_rlimit_value("max", jmax, flags, &rl.rlim_max);
+                if (r < 0)
+                        return r;
+
+                if (limits[l])
+                        *(limits[l]) = rl;
+                else {
+                        limits[l] = newdup(struct rlimit, &rl, 1);
+                        if (!limits[l])
+                                return log_oom();
+                }
+        }
+
+        return 0;
+}
+
+static int json_dispatch_filename_or_path(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        char **s = userdata;
+        const char *n;
+        int r;
+
+        assert(s);
+
+        if (json_variant_is_null(variant)) {
+                *s = mfree(*s);
+                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));
+
+        n = json_variant_string(variant);
+        if (!filename_is_valid(n) && !path_is_normalized(n))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid file name or normalized path.", strna(name));
+
+        r = free_and_strdup(s, n);
+        if (r < 0)
+                return json_log(variant, flags, r, "Failed to allocate string: %m");
+
+        return 0;
+}
+
+static int json_dispatch_path(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        char **s = userdata;
+        const char *n;
+        int r;
+
+        if (json_variant_is_null(variant)) {
+                *s = mfree(*s);
+                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));
+
+        n = json_variant_string(variant);
+        if (!path_is_normalized(n))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a normalized file system path.", strna(name));
+        if (!path_is_absolute(n))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an absolute file system path.", strna(name));
+
+        r = free_and_strdup(s, n);
+        if (r < 0)
+                return json_log(variant, flags, r, "Failed to allocate string: %m");
+
+        return 0;
+}
+
+static int json_dispatch_home_directory(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        char **s = userdata;
+        const char *n;
+        int r;
+
+        if (json_variant_is_null(variant)) {
+                *s = mfree(*s);
+                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));
+
+        n = json_variant_string(variant);
+        if (!valid_home(n))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid home directory path.", strna(name));
+
+        r = free_and_strdup(s, n);
+        if (r < 0)
+                return json_log(variant, flags, r, "Failed to allocate string: %m");
+
+        return 0;
+}
+
+static int json_dispatch_image_path(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        char **s = userdata;
+        const char *n;
+        int r;
+
+        if (json_variant_is_null(variant)) {
+                *s = mfree(*s);
+                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));
+
+        n = json_variant_string(variant);
+        if (empty_or_root(n) || !path_is_valid(n) || !path_is_absolute(n))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid image path.", strna(name));
+
+        r = free_and_strdup(s, n);
+        if (r < 0)
+                return json_log(variant, flags, r, "Failed to allocate string: %m");
+
+        return 0;
+}
+
+static int json_dispatch_umask(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        mode_t *m = userdata;
+        uintmax_t k;
+
+        if (json_variant_is_null(variant)) {
+                *m = (mode_t) -1;
+                return 0;
+        }
+
+        if (!json_variant_is_unsigned(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a number.", strna(name));
+
+        k = json_variant_unsigned(variant);
+        if (k > 0777)
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' outside of valid range 0…0777.", strna(name));
+
+        *m = (mode_t) k;
+        return 0;
+}
+
+static int json_dispatch_access_mode(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        mode_t *m = userdata;
+        uintmax_t k;
+
+        if (json_variant_is_null(variant)) {
+                *m = (mode_t) -1;
+                return 0;
+        }
+
+        if (!json_variant_is_unsigned(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a number.", strna(name));
+
+        k = json_variant_unsigned(variant);
+        if (k > 07777)
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' outside of valid range 0…07777.", strna(name));
+
+        *m = (mode_t) k;
+        return 0;
+}
+
+static int json_dispatch_environment(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        _cleanup_strv_free_ char **n = NULL;
+        char ***l = userdata;
+        size_t i;
+        int r;
+
+        if (json_variant_is_null(variant)) {
+                *l = strv_free(*l);
+                return 0;
+        }
+
+        if (!json_variant_is_array(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
+
+        for (i = 0; i < json_variant_elements(variant); i++) {
+                _cleanup_free_ char *c = NULL;
+                JsonVariant *e;
+                const char *a;
+
+                e = json_variant_by_index(variant, i);
+                if (!json_variant_is_string(e))
+                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name));
+
+                assert_se(a = json_variant_string(e));
+
+                if (!env_assignment_is_valid(a))
+                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of environment variables.", strna(name));
+
+                c = strdup(a);
+                if (!c)
+                        return json_log_oom(variant, flags);
+
+                r = strv_env_replace(&n, c);
+                if (r < 0)
+                        return json_log_oom(variant, flags);
+
+                c = NULL;
+        }
+
+        strv_free_and_replace(*l, n);
+        return 0;
+}
+
+int json_dispatch_user_disposition(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        UserDisposition *disposition = userdata, k;
+
+        if (json_variant_is_null(variant)) {
+                *disposition = _USER_DISPOSITION_INVALID;
+                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));
+
+        k = user_disposition_from_string(json_variant_string(variant));
+        if (k < 0)
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Disposition type '%s' not known.", json_variant_string(variant));
+
+        *disposition = k;
+        return 0;
+}
+
+static int json_dispatch_storage(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        UserStorage *storage = userdata, k;
+
+        if (json_variant_is_null(variant)) {
+                *storage = _USER_STORAGE_INVALID;
+                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));
+
+        k = user_storage_from_string(json_variant_string(variant));
+        if (k < 0)
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Storage type '%s' not known.", json_variant_string(variant));
+
+        *storage = k;
+        return 0;
+}
+
+static int json_dispatch_disk_size(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        uint64_t *size = userdata;
+        uintmax_t k;
+
+        if (json_variant_is_null(variant)) {
+                *size = UINT64_MAX;
+                return 0;
+        }
+
+        if (!json_variant_is_unsigned(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name));
+
+        k = json_variant_unsigned(variant);
+        if (k < USER_DISK_SIZE_MIN || k > USER_DISK_SIZE_MAX)
+                return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' is not in valid range %" PRIu64 "…%" PRIu64 ".", strna(name), USER_DISK_SIZE_MIN, USER_DISK_SIZE_MAX);
+
+        *size = k;
+        return 0;
+}
+
+static int json_dispatch_tasks_or_memory_max(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        uint64_t *limit = userdata;
+        uintmax_t k;
+
+        if (json_variant_is_null(variant)) {
+                *limit = UINT64_MAX;
+                return 0;
+        }
+
+        if (!json_variant_is_unsigned(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a integer.", strna(name));
+
+        k = json_variant_unsigned(variant);
+        if (k <= 0 || k >= UINT64_MAX)
+                return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' is not in valid range %" PRIu64 "…%" PRIu64 ".", strna(name), (uint64_t) 1, UINT64_MAX-1);
+
+        *limit = k;
+        return 0;
+}
+
+static int json_dispatch_weight(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        uint64_t *weight = userdata;
+        uintmax_t k;
+
+        if (json_variant_is_null(variant)) {
+                *weight = UINT64_MAX;
+                return 0;
+        }
+
+        if (!json_variant_is_unsigned(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a integer.", strna(name));
+
+        k = json_variant_unsigned(variant);
+        if (k <= CGROUP_WEIGHT_MIN || k >= CGROUP_WEIGHT_MAX)
+                return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' is not in valid range %" PRIu64 "…%" PRIu64 ".", strna(name), (uint64_t) CGROUP_WEIGHT_MIN, (uint64_t) CGROUP_WEIGHT_MAX);
+
+        *weight = k;
+        return 0;
+}
+
+int json_dispatch_user_group_list(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        _cleanup_strv_free_ char **l = NULL;
+        char ***list = 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) {
+
+                if (!json_variant_is_string(e))
+                        return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a string.");
+
+                if (!valid_user_group_name_compat(json_variant_string(e)))
+                        return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a valid user/group name: %s", json_variant_string(e));
+
+                r = strv_extend(&l, json_variant_string(e));
+                if (r < 0)
+                        return json_log(e, flags, r, "Failed to append array element: %m");
+        }
+
+        r = strv_extend_strv(list, l, true);
+        if (r < 0)
+                return json_log(variant, flags, r, "Failed to merge user/group arrays: %m");
+
+        return 0;
+}
+
+static int dispatch_secret(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+
+        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 },
+                { "pkcs11ProtectedAuthenticationPathPermitted", JSON_VARIANT_BOOLEAN,       json_dispatch_tristate, offsetof(UserRecord, pkcs11_protected_authentication_path_permitted), 0 },
+                {},
+        };
+
+        return json_dispatch(variant, secret_dispatch_table, NULL, flags, userdata);
+}
+
+static int dispatch_pkcs11_uri(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        char **s = userdata;
+        const char *n;
+        int r;
+
+        if (json_variant_is_null(variant)) {
+                *s = mfree(*s);
+                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));
+
+        n = json_variant_string(variant);
+        if (!pkcs11_uri_valid(n))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid RFC7512 PKCS#11 URI.", strna(name));
+
+        r = free_and_strdup(s, n);
+        if (r < 0)
+                return json_log(variant, flags, r, "Failed to allocate string: %m");
+
+        return 0;
+}
+
+static int dispatch_pkcs11_uri_array(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        _cleanup_strv_free_ char **z = NULL;
+        char ***l = userdata;
+        JsonVariant *e;
+        int r;
+
+        if (json_variant_is_null(variant)) {
+                *l = strv_free(*l);
+                return 0;
+        }
+
+        if (json_variant_is_string(variant)) {
+                const char *n;
+
+                n = json_variant_string(variant);
+                if (!pkcs11_uri_valid(n))
+                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid RFC7512 PKCS#11 URI.", strna(name));
+
+                z = strv_new(n);
+                if (!z)
+                        return log_oom();
+
+        } else {
+
+                if (!json_variant_is_array(variant))
+                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string or array of strings.", strna(name));
+
+                JSON_VARIANT_ARRAY_FOREACH(e, variant) {
+                        const char *n;
+
+                        if (!json_variant_is_string(e))
+                                return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a string.");
+
+                        n = json_variant_string(e);
+                        if (!pkcs11_uri_valid(n))
+                                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element in '%s' is not a valid RFC7512 PKCS#11 URI: %s", strna(name), n);
+
+                        r = strv_extend(&z, n);
+                        if (r < 0)
+                                return log_oom();
+                }
+        }
+
+        strv_free_and_replace(*l, z);
+        return 0;
+}
+
+static int dispatch_pkcs11_key_data(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        Pkcs11EncryptedKey *k = userdata;
+        size_t l;
+        void *b;
+        int r;
+
+        if (json_variant_is_null(variant)) {
+                k->data = mfree(k->data);
+                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 encrypted PKCS#11 key: %m");
+
+        erase_and_free(k->data);
+        k->data = b;
+        k->size = l;
+
+        return 0;
+}
+
+static int dispatch_pkcs11_key(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) {
+                Pkcs11EncryptedKey *array, *k;
+
+                static const JsonDispatch pkcs11_key_dispatch_table[] = {
+                        { "uri",            JSON_VARIANT_STRING, dispatch_pkcs11_uri,      offsetof(Pkcs11EncryptedKey, uri),             JSON_MANDATORY },
+                        { "data",           JSON_VARIANT_STRING, dispatch_pkcs11_key_data, 0,                                             JSON_MANDATORY },
+                        { "hashedPassword", JSON_VARIANT_STRING, json_dispatch_string,     offsetof(Pkcs11EncryptedKey, 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->pkcs11_encrypted_key, h->n_pkcs11_encrypted_key + 1, sizeof(Pkcs11EncryptedKey));
+                if (!array)
+                        return log_oom();
+
+                h->pkcs11_encrypted_key = array;
+                k = h->pkcs11_encrypted_key + h->n_pkcs11_encrypted_key;
+                *k = (Pkcs11EncryptedKey) {};
+
+                r = json_dispatch(e, pkcs11_key_dispatch_table, NULL, flags, k);
+                if (r < 0) {
+                        pkcs11_encrypted_key_done(k);
+                        return r;
+                }
+
+                h->n_pkcs11_encrypted_key++;
+        }
+
+        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         },
+                {},
+        };
+
+        return json_dispatch(variant, privileged_dispatch_table, NULL, flags, userdata);
+}
+
+static int dispatch_binding(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+
+        static const JsonDispatch binding_dispatch_table[] = {
+                { "imagePath",         JSON_VARIANT_STRING,        json_dispatch_image_path,     offsetof(UserRecord, image_path),           0         },
+                { "homeDirectory",     JSON_VARIANT_STRING,        json_dispatch_home_directory, offsetof(UserRecord, home_directory),       0         },
+                { "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         },
+                { "uid",               JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,        offsetof(UserRecord, uid),                  0         },
+                { "gid",               JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,        offsetof(UserRecord, gid),                  0         },
+                { "storage",           JSON_VARIANT_STRING,        json_dispatch_storage,        offsetof(UserRecord, storage),              0         },
+                { "fileSystemType",    JSON_VARIANT_STRING,        json_dispatch_string,         offsetof(UserRecord, file_system_type),     JSON_SAFE },
+                { "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         },
+                {},
+        };
+
+        char smid[SD_ID128_STRING_MAX];
+        JsonVariant *m;
+        sd_id128_t mid;
+        int r;
+
+        if (!variant)
+                return 0;
+
+        if (!json_variant_is_object(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
+
+        r = sd_id128_get_machine(&mid);
+        if (r < 0)
+                return json_log(variant, flags, r, "Failed to determine machine ID: %m");
+
+        m = json_variant_by_key(variant, sd_id128_to_string(mid, smid));
+        if (!m)
+                return 0;
+
+        return json_dispatch(m, binding_dispatch_table, NULL, flags, userdata);
+}
+
+int per_machine_id_match(JsonVariant *ids, JsonDispatchFlags flags) {
+        sd_id128_t mid;
+        int r;
+
+        r = sd_id128_get_machine(&mid);
+        if (r < 0)
+                return json_log(ids, flags, r, "Failed to acquire machine ID: %m");
+
+        if (json_variant_is_string(ids)) {
+                sd_id128_t k;
+
+                r = sd_id128_from_string(json_variant_string(ids), &k);
+                if (r < 0) {
+                        json_log(ids, flags, r, "%s is not a valid machine ID, ignoring: %m", json_variant_string(ids));
+                        return 0;
+                }
+
+                return sd_id128_equal(mid, k);
+        }
+
+        if (json_variant_is_array(ids)) {
+                JsonVariant *e;
+
+                JSON_VARIANT_ARRAY_FOREACH(e, ids) {
+                        sd_id128_t k;
+
+                        if (!json_variant_is_string(e)) {
+                                json_log(e, flags, 0, "Machine ID is not a string, ignoring: %m");
+                                continue;
+                        }
+
+                        r = sd_id128_from_string(json_variant_string(e), &k);
+                        if (r < 0) {
+                                json_log(e, flags, r, "%s is not a valid machine ID, ignoring: %m", json_variant_string(e));
+                                continue;
+                        }
+
+                        if (sd_id128_equal(mid, k))
+                                return true;
+                }
+
+                return false;
+        }
+
+        json_log(ids, flags, 0, "Machine ID is not a string or array of strings, ignoring: %m");
+        return false;
+}
+
+int per_machine_hostname_match(JsonVariant *hns, JsonDispatchFlags flags) {
+        _cleanup_free_ char *hn = NULL;
+        int r;
+
+        r = gethostname_strict(&hn);
+        if (r == -ENXIO) {
+                json_log(hns, flags, r, "No hostname set, not matching perMachine hostname record: %m");
+                return false;
+        }
+        if (r < 0)
+                return json_log(hns, flags, r, "Failed to acquire hostname: %m");
+
+        if (json_variant_is_string(hns))
+                return streq(json_variant_string(hns), hn);
+
+        if (json_variant_is_array(hns)) {
+                JsonVariant *e;
+
+                JSON_VARIANT_ARRAY_FOREACH(e, hns) {
+
+                        if (!json_variant_is_string(e)) {
+                                json_log(e, flags, 0, "Hostname is not a string, ignoring: %m");
+                                continue;
+                        }
+
+                        if (streq(json_variant_string(hns), hn))
+                                return true;
+                }
+
+                return false;
+        }
+
+        json_log(hns, flags, 0, "Hostname is not a string or array of strings, ignoring: %m");
+        return false;
+}
+
+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),                     0         },
+                { "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         },
+                {},
+        };
+
+        JsonVariant *e;
+        int r;
+
+        if (!variant)
+                return 0;
+
+        if (!json_variant_is_array(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
+
+        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
+                bool matching = false;
+                JsonVariant *m;
+
+                if (!json_variant_is_object(e))
+                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of objects.", strna(name));
+
+                m = json_variant_by_key(e, "matchMachineId");
+                if (m) {
+                        r = per_machine_id_match(m, flags);
+                        if (r < 0)
+                                return r;
+
+                        matching = r > 0;
+                }
+
+                if (!matching) {
+                        m = json_variant_by_key(e, "matchHostname");
+                        if (m) {
+                                r = per_machine_hostname_match(m, flags);
+                                if (r < 0)
+                                        return r;
+
+                                matching = r > 0;
+                        }
+                }
+
+                if (!matching)
+                        continue;
+
+                r = json_dispatch(e, per_machine_dispatch_table, NULL, flags, userdata);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int dispatch_status(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+
+        static const JsonDispatch status_dispatch_table[] = {
+                { "diskUsage",                  JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,   offsetof(UserRecord, disk_usage),                    0         },
+                { "diskFree",                   JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,   offsetof(UserRecord, disk_free),                     0         },
+                { "diskSize",                   JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,   offsetof(UserRecord, disk_size),                     0         },
+                { "diskCeiling",                JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,   offsetof(UserRecord, disk_ceiling),                  0         },
+                { "diskFloor",                  JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,   offsetof(UserRecord, disk_floor),                    0         },
+                { "state",                      JSON_VARIANT_STRING,        json_dispatch_string,   offsetof(UserRecord, state),                         JSON_SAFE },
+                { "service",                    JSON_VARIANT_STRING,        json_dispatch_string,   offsetof(UserRecord, service),                       JSON_SAFE },
+                { "signedLocally",              _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, signed_locally),                0         },
+                { "goodAuthenticationCounter",  JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,   offsetof(UserRecord, good_authentication_counter),   0         },
+                { "badAuthenticationCounter",   JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,   offsetof(UserRecord, bad_authentication_counter),    0         },
+                { "lastGoodAuthenticationUSec", JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,   offsetof(UserRecord, last_good_authentication_usec), 0         },
+                { "lastBadAuthenticationUSec",  JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,   offsetof(UserRecord, last_bad_authentication_usec),  0         },
+                { "rateLimitBeginUSec",         JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,   offsetof(UserRecord, ratelimit_begin_usec),          0         },
+                { "rateLimitCount",             JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,   offsetof(UserRecord, ratelimit_count),               0         },
+                { "removable",                  JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,  offsetof(UserRecord, removable),                     0         },
+                {},
+        };
+
+        char smid[SD_ID128_STRING_MAX];
+        JsonVariant *m;
+        sd_id128_t mid;
+        int r;
+
+        if (!variant)
+                return 0;
+
+        if (!json_variant_is_object(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
+
+        r = sd_id128_get_machine(&mid);
+        if (r < 0)
+                return json_log(variant, flags, r, "Failed to determine machine ID: %m");
+
+        m = json_variant_by_key(variant, sd_id128_to_string(mid, smid));
+        if (!m)
+                return 0;
+
+        return json_dispatch(m, status_dispatch_table, NULL, flags, userdata);
+}
+
+static int user_record_augment(UserRecord *h, JsonDispatchFlags json_flags) {
+        assert(h);
+
+        if (!FLAGS_SET(h->mask, USER_RECORD_REGULAR))
+                return 0;
+
+        assert(h->user_name);
+
+        if (!h->user_name_and_realm_auto && h->realm) {
+                h->user_name_and_realm_auto = strjoin(h->user_name, "@", h->realm);
+                if (!h->user_name_and_realm_auto)
+                        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 */
+        if (user_record_disposition(h) != USER_REGULAR)
+                return 0;
+
+        if (!h->home_directory && !h->home_directory_auto) {
+                h->home_directory_auto = path_join("/home/", h->user_name);
+                if (!h->home_directory_auto)
+                        return json_log_oom(h->json, 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);
+                }
+        }
+
+        return 0;
+}
+
+int user_group_record_mangle(
+                JsonVariant *v,
+                UserRecordLoadFlags load_flags,
+                JsonVariant **ret_variant,
+                UserRecordMask *ret_mask) {
+
+        static const struct {
+                UserRecordMask mask;
+                const char *name;
+        } mask_field[] = {
+                { USER_RECORD_PRIVILEGED,  "privileged" },
+                { USER_RECORD_SECRET,      "secret"     },
+                { USER_RECORD_BINDING,     "binding"    },
+                { USER_RECORD_PER_MACHINE, "perMachine" },
+                { USER_RECORD_STATUS,      "status"     },
+                { USER_RECORD_SIGNATURE,   "signature"  },
+        };
+
+        JsonDispatchFlags json_flags = USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(load_flags);
+        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+        JsonVariant *array[ELEMENTSOF(mask_field) * 2];
+        size_t n_retain = 0, i;
+        UserRecordMask m = 0;
+        int r;
+
+        assert((load_flags & _USER_RECORD_MASK_MAX) == 0); /* detect mistakes when accidentally passing
+                                                            * UserRecordMask bit masks as UserRecordLoadFlags
+                                                            * value */
+
+        assert(v);
+        assert(ret_variant);
+        assert(ret_mask);
+
+        /* Note that this function is shared with the group record parser, hence we try to be generic in our
+         * log message wording here, to cover both cases. */
+
+        if (!json_variant_is_object(v))
+                return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record is not a JSON object, refusing.");
+
+        if (USER_RECORD_ALLOW_MASK(load_flags) == 0) /* allow nothing? */
+                return json_log(v, json_flags, SYNTHETIC_ERRNO(EINVAL), "Nothing allowed in record, refusing.");
+
+        if (USER_RECORD_STRIP_MASK(load_flags) == _USER_RECORD_MASK_MAX) /* strip everything? */
+                return json_log(v, json_flags, SYNTHETIC_ERRNO(EINVAL), "Stripping everything from record, refusing.");
+
+        /* Check if we have the special sections and if they match our flags set */
+        for (i = 0; i < ELEMENTSOF(mask_field); i++) {
+                JsonVariant *e, *k;
+
+                if (FLAGS_SET(USER_RECORD_STRIP_MASK(load_flags), mask_field[i].mask)) {
+                        if (!w)
+                                w = json_variant_ref(v);
+
+                        r = json_variant_filter(&w, STRV_MAKE(mask_field[i].name));
+                        if (r < 0)
+                                return json_log(w, json_flags, r, "Failed to remove field from variant: %m");
+
+                        continue;
+                }
+
+                e = json_variant_by_key_full(v, mask_field[i].name, &k);
+                if (e) {
+                        if (!FLAGS_SET(USER_RECORD_ALLOW_MASK(load_flags), mask_field[i].mask))
+                                return json_log(e, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record contains '%s' field, which is not allowed.", mask_field[i].name);
+
+                        if (FLAGS_SET(load_flags, USER_RECORD_STRIP_REGULAR)) {
+                                array[n_retain++] = k;
+                                array[n_retain++] = e;
+                        }
+
+                        m |= mask_field[i].mask;
+                } else {
+                        if (FLAGS_SET(USER_RECORD_REQUIRE_MASK(load_flags), mask_field[i].mask))
+                                return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record lacks '%s' field, which is required.", mask_field[i].name);
+                }
+        }
+
+        if (FLAGS_SET(load_flags, USER_RECORD_STRIP_REGULAR)) {
+                /* If we are supposed to strip regular items, then let's instead just allocate a new object
+                 * with just the stuff we need. */
+
+                w = json_variant_unref(w);
+                r = json_variant_new_object(&w, array, n_retain);
+                if (r < 0)
+                        return json_log(v, json_flags, r, "Failed to allocate new object: %m");
+        } else {
+                /* And now check if there's anything else in the record */
+                for (i = 0; i < json_variant_elements(v); i += 2) {
+                        const char *f;
+                        bool special = false;
+                        size_t j;
+
+                        assert_se(f = json_variant_string(json_variant_by_index(v, i)));
+
+                        for (j = 0; j < ELEMENTSOF(mask_field); j++)
+                                if (streq(f, mask_field[j].name)) { /* already covered in the loop above */
+                                        special = true;
+                                        continue;
+                                }
+
+                        if (!special) {
+                                if ((load_flags & (USER_RECORD_ALLOW_REGULAR|USER_RECORD_REQUIRE_REGULAR)) == 0)
+                                        return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record contains '%s' field, which is not allowed.", f);
+
+                                m |= USER_RECORD_REGULAR;
+                                break;
+                        }
+                }
+        }
+
+        if (FLAGS_SET(load_flags, USER_RECORD_REQUIRE_REGULAR) && !FLAGS_SET(m, USER_RECORD_REGULAR))
+                return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record lacks basic identity fields, which are required.");
+
+        if (m == 0)
+                return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record is empty.");
+
+        if (w)
+                *ret_variant = TAKE_PTR(w);
+        else
+                *ret_variant = json_variant_ref(v);
+
+        *ret_mask = m;
+        return 0;
+}
+
+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),                     0         },
+                { "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),                     0         },
+                { "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         },
+
+                /* 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         },
+
+                /* Ignore 'signature', we check it with explicit accessors instead */
+                { "signature",                  JSON_VARIANT_ARRAY,         NULL,                              0,                                                   0         },
+                {},
+        };
+
+        JsonDispatchFlags json_flags = USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(load_flags);
+        int r;
+
+        assert(h);
+        assert(!h->json);
+
+        /* Note that this call will leave a half-initialized record around on failure! */
+
+        r = user_group_record_mangle(v, load_flags, &h->json, &h->mask);
+        if (r < 0)
+                return r;
+
+        r = json_dispatch(h->json, user_dispatch_table, NULL, json_flags, h);
+        if (r < 0)
+                return r;
+
+        /* During the parsing operation above we ignored the 'perMachine', 'binding' and 'status' fields,
+         * since we want them to override the global options. Let's process them now. */
+
+        r = dispatch_per_machine("perMachine", json_variant_by_key(h->json, "perMachine"), json_flags, h);
+        if (r < 0)
+                return r;
+
+        r = dispatch_binding("binding", json_variant_by_key(h->json, "binding"), json_flags, h);
+        if (r < 0)
+                return r;
+
+        r = dispatch_status("status", json_variant_by_key(h->json, "status"), json_flags, h);
+        if (r < 0)
+                return r;
+
+        if (FLAGS_SET(h->mask, USER_RECORD_REGULAR) && !h->user_name)
+                return json_log(h->json, json_flags, SYNTHETIC_ERRNO(EINVAL), "User name field missing, refusing.");
+
+        r = user_record_augment(h, json_flags);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+int user_record_build(UserRecord **ret, ...) {
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        _cleanup_(user_record_unrefp) UserRecord *u = NULL;
+        va_list ap;
+        int r;
+
+        assert(ret);
+
+        va_start(ap, ret);
+        r = json_buildv(&v, ap);
+        va_end(ap);
+
+        if (r < 0)
+                return r;
+
+        u = user_record_new();
+        if (!u)
+                return -ENOMEM;
+
+        r = user_record_load(u, v, USER_RECORD_LOAD_FULL);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(u);
+        return 0;
+}
+
+const char *user_record_user_name_and_realm(UserRecord *h) {
+        assert(h);
+
+        /* Return the pre-initialized joined string if it is defined */
+        if (h->user_name_and_realm_auto)
+                return h->user_name_and_realm_auto;
+
+        /* If it's not defined then we cannot have a realm */
+        assert(!h->realm);
+        return h->user_name;
+}
+
+UserStorage user_record_storage(UserRecord *h) {
+        assert(h);
+
+        if (h->storage >= 0)
+                return h->storage;
+
+        return USER_CLASSIC;
+}
+
+const char *user_record_file_system_type(UserRecord *h) {
+        assert(h);
+
+        return h->file_system_type ?: "ext4";
+}
+
+const char *user_record_skeleton_directory(UserRecord *h) {
+        assert(h);
+
+        return h->skeleton_directory ?: "/etc/skel";
+}
+
+mode_t user_record_access_mode(UserRecord *h) {
+        assert(h);
+
+        return h->access_mode != (mode_t) -1 ? h->access_mode : 0700;
+}
+
+const char* user_record_home_directory(UserRecord *h) {
+        assert(h);
+
+        if (h->home_directory)
+                return h->home_directory;
+        if (h->home_directory_auto)
+                return h->home_directory_auto;
+
+        /* The root user is special, hence be special about it */
+        if (streq_ptr(h->user_name, "root"))
+                return "/root";
+
+        return "/";
+}
+
+const char *user_record_image_path(UserRecord *h) {
+        assert(h);
+
+        if (h->image_path)
+                return h->image_path;
+        if (h->image_path_auto)
+                return h->image_path_auto;
+
+        return IN_SET(user_record_storage(h), USER_CLASSIC, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT) ? user_record_home_directory(h) : NULL;
+}
+
+const char *user_record_cifs_user_name(UserRecord *h) {
+        assert(h);
+
+        return h->cifs_user_name ?: h->user_name;
+}
+
+unsigned long user_record_mount_flags(UserRecord *h) {
+        assert(h);
+
+        return (h->nosuid ? MS_NOSUID : 0) |
+                (h->noexec ? MS_NOEXEC : 0) |
+                (h->nodev ? MS_NODEV : 0);
+}
+
+const char *user_record_shell(UserRecord *h) {
+        assert(h);
+
+        if (h->shell)
+                return h->shell;
+
+        if (streq_ptr(h->user_name, "root"))
+                return "/bin/sh";
+
+        if (user_record_disposition(h) == USER_REGULAR)
+                return "/bin/bash";
+
+        return NOLOGIN;
+}
+
+const char *user_record_real_name(UserRecord *h) {
+        assert(h);
+
+        return h->real_name ?: h->user_name;
+}
+
+bool user_record_luks_discard(UserRecord *h) {
+        const char *ip;
+
+        assert(h);
+
+        if (h->luks_discard >= 0)
+                return h->luks_discard;
+
+        ip = user_record_image_path(h);
+        if (!ip)
+                return false;
+
+        /* Use discard by default if we are referring to a real block device, but not when operating on a
+         * loopback device. We want to optimize for SSD and flash storage after all, but we should be careful
+         * when storing stuff on top of regular file systems in loopback files as doing discard then would
+         * mean thin provisioning and we should not do that willy-nilly since it means we'll risk EIO later
+         * on should the disk space to back our file systems not be available. */
+
+        return path_startswith(ip, "/dev/");
+}
+
+const char *user_record_luks_cipher(UserRecord *h) {
+        assert(h);
+
+        return h->luks_cipher ?: "aes";
+}
+
+const char *user_record_luks_cipher_mode(UserRecord *h) {
+        assert(h);
+
+        return h->luks_cipher_mode ?: "xts-plain64";
+}
+
+uint64_t user_record_luks_volume_key_size(UserRecord *h) {
+        assert(h);
+
+        /* We return a value here that can be cast without loss into size_t which is what libcrypsetup expects */
+
+        if (h->luks_volume_key_size == UINT64_MAX)
+                return 256 / 8;
+
+        return MIN(h->luks_volume_key_size, SIZE_MAX);
+}
+
+const char* user_record_luks_pbkdf_type(UserRecord *h) {
+        assert(h);
+
+        return h->luks_pbkdf_type ?: "argon2i";
+}
+
+uint64_t user_record_luks_pbkdf_time_cost_usec(UserRecord *h) {
+        assert(h);
+
+        /* Returns a value with ms granularity, since that's what libcryptsetup expects */
+
+        if (h->luks_pbkdf_time_cost_usec == UINT64_MAX)
+                return 500 * USEC_PER_MSEC; /* We default to 500ms, in contrast to libcryptsetup's 2s, which is just awfully slow on every login */
+
+        return MIN(DIV_ROUND_UP(h->luks_pbkdf_time_cost_usec, USEC_PER_MSEC), UINT32_MAX) * USEC_PER_MSEC;
+}
+
+uint64_t user_record_luks_pbkdf_memory_cost(UserRecord *h) {
+        assert(h);
+
+        /* Returns a value with kb granularity, since that's what libcryptsetup expects */
+
+        if (h->luks_pbkdf_memory_cost == UINT64_MAX)
+                return 64*1024*1024; /* We default to 64M, since this should work on smaller systems too */
+
+        return MIN(DIV_ROUND_UP(h->luks_pbkdf_memory_cost, 1024), UINT32_MAX) * 1024;
+}
+
+uint64_t user_record_luks_pbkdf_parallel_threads(UserRecord *h) {
+        assert(h);
+
+        if (h->luks_pbkdf_memory_cost == UINT64_MAX)
+                return 1; /* We default to 1, since this should work on smaller systems too */
+
+        return MIN(h->luks_pbkdf_parallel_threads, UINT32_MAX);
+}
+
+const char *user_record_luks_pbkdf_hash_algorithm(UserRecord *h) {
+        assert(h);
+
+        return h->luks_pbkdf_hash_algorithm ?: "sha512";
+}
+
+gid_t user_record_gid(UserRecord *h) {
+        assert(h);
+
+        if (gid_is_valid(h->gid))
+                return h->gid;
+
+        return (gid_t) h->uid;
+}
+
+UserDisposition user_record_disposition(UserRecord *h) {
+        assert(h);
+
+        if (h->disposition >= 0)
+                return h->disposition;
+
+        /* If not declared, derive from UID */
+
+        if (!uid_is_valid(h->uid))
+                return _USER_DISPOSITION_INVALID;
+
+        if (h->uid == 0 || h->uid == UID_NOBODY)
+                return USER_INTRINSIC;
+
+        if (uid_is_system(h->uid))
+                return USER_SYSTEM;
+
+        if (uid_is_dynamic(h->uid))
+                return USER_DYNAMIC;
+
+        if (uid_is_container(h->uid))
+                return USER_CONTAINER;
+
+        if (h->uid > INT32_MAX)
+                return USER_RESERVED;
+
+        return USER_REGULAR;
+}
+
+int user_record_removable(UserRecord *h) {
+        UserStorage storage;
+        assert(h);
+
+        if (h->removable >= 0)
+                return h->removable;
+
+        /* Refuse to decide for classic records */
+        storage = user_record_storage(h);
+        if (h->storage < 0 || h->storage == USER_CLASSIC)
+                return -1;
+
+        /* For now consider only LUKS home directories with a reference by path as removable */
+        return storage == USER_LUKS && path_startswith(user_record_image_path(h), "/dev/");
+}
+
+uint64_t user_record_ratelimit_interval_usec(UserRecord *h) {
+        assert(h);
+
+        if (h->ratelimit_interval_usec == UINT64_MAX)
+                return DEFAULT_RATELIMIT_INTERVAL_USEC;
+
+        return h->ratelimit_interval_usec;
+}
+
+uint64_t user_record_ratelimit_burst(UserRecord *h) {
+        assert(h);
+
+        if (h->ratelimit_burst == UINT64_MAX)
+                return DEFAULT_RATELIMIT_BURST;
+
+        return h->ratelimit_burst;
+}
+
+bool user_record_can_authenticate(UserRecord *h) {
+        assert(h);
+
+        /* Returns true if there's some form of property configured that the user can authenticate against */
+
+        if (h->n_pkcs11_encrypted_key > 0)
+                return true;
+
+        return !strv_isempty(h->hashed_password);
+}
+
+uint64_t user_record_ratelimit_next_try(UserRecord *h) {
+        assert(h);
+
+        /* Calculates when the it's possible to login next. Returns:
+         *
+         * UINT64_MAX → Nothing known
+         * 0          → Right away
+         * Any other  → Next time in CLOCK_REALTIME in usec (which could be in the past)
+         */
+
+        if (h->ratelimit_begin_usec == UINT64_MAX ||
+            h->ratelimit_count == UINT64_MAX)
+                return UINT64_MAX;
+
+        if (h->ratelimit_count < user_record_ratelimit_burst(h))
+                return 0;
+
+        return usec_add(h->ratelimit_begin_usec, user_record_ratelimit_interval_usec(h));
+}
+
+bool user_record_equal(UserRecord *a, UserRecord *b) {
+        assert(a);
+        assert(b);
+
+        /* We assume that when a record is modified its JSON data is updated at the same time, hence it's
+         * sufficient to compare the JSON data. */
+
+        return json_variant_equal(a->json, b->json);
+}
+
+bool user_record_compatible(UserRecord *a, UserRecord *b) {
+        assert(a);
+        assert(b);
+
+        /* If either lacks a the regular section, we can't really decide, let's hence say they are
+         * incompatible. */
+        if (!(a->mask & b->mask & USER_RECORD_REGULAR))
+                return false;
+
+        return streq_ptr(a->user_name, b->user_name) &&
+                streq_ptr(a->realm, b->realm);
+}
+
+int user_record_compare_last_change(UserRecord *a, UserRecord *b) {
+        assert(a);
+        assert(b);
+
+        if (a->last_change_usec == b->last_change_usec)
+                return 0;
+
+        /* Always consider a record with a timestamp newer than one without */
+        if (a->last_change_usec == UINT64_MAX)
+                return -1;
+        if (b->last_change_usec == UINT64_MAX)
+                return 1;
+
+        return CMP(a->last_change_usec, b->last_change_usec);
+}
+
+int user_record_clone(UserRecord *h, UserRecordLoadFlags flags, UserRecord **ret) {
+        _cleanup_(user_record_unrefp) UserRecord *c = NULL;
+        int r;
+
+        assert(h);
+        assert(ret);
+
+        c = user_record_new();
+        if (!c)
+                return -ENOMEM;
+
+        r = user_record_load(c, h->json, flags);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(c);
+        return 0;
+}
+
+int user_record_masked_equal(UserRecord *a, UserRecord *b, UserRecordMask mask) {
+        _cleanup_(user_record_unrefp) UserRecord *x = NULL, *y = NULL;
+        int r;
+
+        assert(a);
+        assert(b);
+
+        /* Compares the two records, but ignores anything not listed in the specified mask */
+
+        if ((a->mask & ~mask) != 0) {
+                r = user_record_clone(a, USER_RECORD_ALLOW(mask) | USER_RECORD_STRIP(~mask & _USER_RECORD_MASK_MAX), &x);
+                if (r < 0)
+                        return r;
+
+                a = x;
+        }
+
+        if ((b->mask & ~mask) != 0) {
+                r = user_record_clone(b, USER_RECORD_ALLOW(mask) | USER_RECORD_STRIP(~mask & _USER_RECORD_MASK_MAX), &y);
+                if (r < 0)
+                        return r;
+
+                b = y;
+        }
+
+        return user_record_equal(a, b);
+}
+
+int user_record_test_blocked(UserRecord *h) {
+        usec_t n;
+
+        /* Checks whether access to the specified user shall be allowed at the moment. Returns:
+         *
+         *          -ESTALE: Record is from the future
+         *          -ENOLCK: Record is blocked
+         *          -EL2HLT: Record is not valid yet
+         *          -EL3HLT: Record is not valid anymore
+         *
+         */
+
+        assert(h);
+
+        n = now(CLOCK_REALTIME);
+        if (h->last_change_usec != UINT64_MAX &&
+            h->last_change_usec > n) /* Don't allow log ins when the record is from the future */
+                return -ESTALE;
+
+        if (h->locked > 0)
+                return -ENOLCK;
+
+        if (h->not_before_usec != UINT64_MAX && n < h->not_before_usec)
+                return -EL2HLT;
+        if (h->not_after_usec != UINT64_MAX && n > h->not_after_usec)
+                return -EL3HLT;
+
+        return 0;
+}
+
+int user_record_test_password_change_required(UserRecord *h) {
+        bool change_permitted;
+        usec_t n;
+
+        assert(h);
+
+        /* Checks whether the user must change the password when logging in
+
+            -EKEYREVOKED: Change password now because admin said so
+             -EOWNERDEAD: Change password now because it expired
+           -EKEYREJECTED: Password is expired, no changing is allowed
+            -EKEYEXPIRED: Password is about to expire, warn user
+               -ENETDOWN: Record has expiration info but no password change timestamp
+                  -EROFS: No password change required nor permitted
+                       0: No password change required, but permitted
+         */
+
+        /* If a pasword change request has been set explicitly, it overrides everything */
+        if (h->password_change_now > 0)
+                return -EKEYREVOKED;
+
+        n = now(CLOCK_REALTIME);
+
+        /* Then, let's check if password changing is currently allowed at all */
+        if (h->password_change_min_usec != UINT64_MAX) {
+
+                /* Expiry configured but no password change timestamp known? */
+                if (h->last_password_change_usec == UINT64_MAX)
+                        return -ENETDOWN;
+
+                if (h->password_change_min_usec >= UINT64_MAX - h->last_password_change_usec)
+                        change_permitted = false;
+                else
+                        change_permitted = n >= h->last_password_change_usec + h->password_change_min_usec;
+
+        } else
+                change_permitted = true;
+
+        /* Let's check whether the password has expired.  */
+        if (!(h->password_change_max_usec == UINT64_MAX ||
+              h->password_change_max_usec >= UINT64_MAX - h->last_password_change_usec)) {
+
+                uint64_t change_before;
+
+                /* Expiry configured but no password change timestamp known? */
+                if (h->last_password_change_usec == UINT64_MAX)
+                        return -ENETDOWN;
+
+                /* Password is in inactive phase? */
+                if (h->password_change_inactive_usec != UINT64_MAX &&
+                    h->password_change_inactive_usec < UINT64_MAX - h->password_change_max_usec) {
+                        usec_t added;
+
+                        added = h->password_change_inactive_usec + h->password_change_max_usec;
+                        if (added < UINT64_MAX - h->last_password_change_usec &&
+                            n >= h->last_password_change_usec + added)
+                                return -EKEYREJECTED;
+                }
+
+                /* Password needs to be changed now? */
+                change_before = h->last_password_change_usec + h->password_change_max_usec;
+                if (n >= change_before)
+                        return change_permitted ? -EOWNERDEAD : -EKEYREJECTED;
+
+                /* Warn user? */
+                if (h->password_change_warn_usec != UINT64_MAX &&
+                    (change_before < h->password_change_warn_usec ||
+                     n >= change_before - h->password_change_warn_usec))
+                        return change_permitted ? -EKEYEXPIRED : -EROFS;
+        }
+
+        /* No password changing necessary */
+        return change_permitted ? 0 : -EROFS;
+}
+
+static const char* const user_storage_table[_USER_STORAGE_MAX] = {
+        [USER_CLASSIC]   = "classic",
+        [USER_LUKS]      = "luks",
+        [USER_DIRECTORY] = "directory",
+        [USER_SUBVOLUME] = "subvolume",
+        [USER_FSCRYPT]   = "fscrypt",
+        [USER_CIFS]      = "cifs",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(user_storage, UserStorage);
+
+static const char* const user_disposition_table[_USER_DISPOSITION_MAX] = {
+        [USER_INTRINSIC] = "intrinsic",
+        [USER_SYSTEM]    = "system",
+        [USER_DYNAMIC]   = "dynamic",
+        [USER_REGULAR]   = "regular",
+        [USER_CONTAINER] = "container",
+        [USER_RESERVED]  = "reserved",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(user_disposition, UserDisposition);
diff --git a/src/shared/user-record.h b/src/shared/user-record.h
new file mode 100644 (file)
index 0000000..5bac304
--- /dev/null
@@ -0,0 +1,375 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include "sd-id128.h"
+
+#include "json.h"
+#include "missing_resource.h"
+#include "time-util.h"
+
+/* But some limits on disk sizes: not less than 5M, not more than 5T */
+#define USER_DISK_SIZE_MIN (UINT64_C(5)*1024*1024)
+#define USER_DISK_SIZE_MAX (UINT64_C(5)*1024*1024*1024*1024)
+
+/* The default disk size to use when nothing else is specified, relative to free disk space */
+#define USER_DISK_SIZE_DEFAULT_PERCENT 85
+
+typedef enum UserDisposition {
+        USER_INTRINSIC,   /* root and nobody */
+        USER_SYSTEM,      /* statically allocated users for system services */
+        USER_DYNAMIC,     /* dynamically allocated users for system services */
+        USER_REGULAR,     /* regular (typically human users) */
+        USER_CONTAINER,   /* UID ranges allocated for container uses */
+        USER_RESERVED,    /* Range above 2^31 */
+        _USER_DISPOSITION_MAX,
+        _USER_DISPOSITION_INVALID = -1,
+} UserDisposition;
+
+typedef enum UserHomeStorage {
+        USER_CLASSIC,
+        USER_LUKS,
+        USER_DIRECTORY, /* A directory, and a .identity file in it, which USER_CLASSIC lacks */
+        USER_SUBVOLUME,
+        USER_FSCRYPT,
+        USER_CIFS,
+        _USER_STORAGE_MAX,
+        _USER_STORAGE_INVALID = -1
+} UserStorage;
+
+typedef enum UserRecordMask {
+        /* The various sections an identity record may have, as bit mask */
+        USER_RECORD_REGULAR     = 1U << 0,
+        USER_RECORD_SECRET      = 1U << 1,
+        USER_RECORD_PRIVILEGED  = 1U << 2,
+        USER_RECORD_PER_MACHINE = 1U << 3,
+        USER_RECORD_BINDING     = 1U << 4,
+        USER_RECORD_STATUS      = 1U << 5,
+        USER_RECORD_SIGNATURE   = 1U << 6,
+        _USER_RECORD_MASK_MAX   = (1U << 7)-1
+} UserRecordMask;
+
+typedef enum UserRecordLoadFlags {
+        /* A set of flags used while loading a user record from JSON data. We leave the lower 6 bits free,
+         * just as a safety precaution so that we can detect borked conversions between UserRecordMask and
+         * UserRecordLoadFlags. */
+
+        /* What to require */
+        USER_RECORD_REQUIRE_REGULAR     = USER_RECORD_REGULAR     << 7,
+        USER_RECORD_REQUIRE_SECRET      = USER_RECORD_SECRET      << 7,
+        USER_RECORD_REQUIRE_PRIVILEGED  = USER_RECORD_PRIVILEGED  << 7,
+        USER_RECORD_REQUIRE_PER_MACHINE = USER_RECORD_PER_MACHINE << 7,
+        USER_RECORD_REQUIRE_BINDING     = USER_RECORD_BINDING     << 7,
+        USER_RECORD_REQUIRE_STATUS      = USER_RECORD_STATUS      << 7,
+        USER_RECORD_REQUIRE_SIGNATURE   = USER_RECORD_SIGNATURE   << 7,
+
+        /* What to allow */
+        USER_RECORD_ALLOW_REGULAR       = USER_RECORD_REGULAR     << 14,
+        USER_RECORD_ALLOW_SECRET        = USER_RECORD_SECRET      << 14,
+        USER_RECORD_ALLOW_PRIVILEGED    = USER_RECORD_PRIVILEGED  << 14,
+        USER_RECORD_ALLOW_PER_MACHINE   = USER_RECORD_PER_MACHINE << 14,
+        USER_RECORD_ALLOW_BINDING       = USER_RECORD_BINDING     << 14,
+        USER_RECORD_ALLOW_STATUS        = USER_RECORD_STATUS      << 14,
+        USER_RECORD_ALLOW_SIGNATURE     = USER_RECORD_SIGNATURE   << 14,
+
+        /* What to strip */
+        USER_RECORD_STRIP_REGULAR       = USER_RECORD_REGULAR     << 21,
+        USER_RECORD_STRIP_SECRET        = USER_RECORD_SECRET      << 21,
+        USER_RECORD_STRIP_PRIVILEGED    = USER_RECORD_PRIVILEGED  << 21,
+        USER_RECORD_STRIP_PER_MACHINE   = USER_RECORD_PER_MACHINE << 21,
+        USER_RECORD_STRIP_BINDING       = USER_RECORD_BINDING     << 21,
+        USER_RECORD_STRIP_STATUS        = USER_RECORD_STATUS      << 21,
+        USER_RECORD_STRIP_SIGNATURE     = USER_RECORD_SIGNATURE   << 21,
+
+        /* Some special combinations that deserve explicit names */
+        USER_RECORD_LOAD_FULL           = USER_RECORD_REQUIRE_REGULAR |
+                                          USER_RECORD_ALLOW_SECRET |
+                                          USER_RECORD_ALLOW_PRIVILEGED |
+                                          USER_RECORD_ALLOW_PER_MACHINE |
+                                          USER_RECORD_ALLOW_BINDING |
+                                          USER_RECORD_ALLOW_STATUS |
+                                          USER_RECORD_ALLOW_SIGNATURE,
+
+        USER_RECORD_LOAD_REFUSE_SECRET =  USER_RECORD_REQUIRE_REGULAR |
+                                          USER_RECORD_ALLOW_PRIVILEGED |
+                                          USER_RECORD_ALLOW_PER_MACHINE |
+                                          USER_RECORD_ALLOW_BINDING |
+                                          USER_RECORD_ALLOW_STATUS |
+                                          USER_RECORD_ALLOW_SIGNATURE,
+
+        USER_RECORD_LOAD_MASK_SECRET =    USER_RECORD_REQUIRE_REGULAR |
+                                          USER_RECORD_ALLOW_PRIVILEGED |
+                                          USER_RECORD_ALLOW_PER_MACHINE |
+                                          USER_RECORD_ALLOW_BINDING |
+                                          USER_RECORD_ALLOW_STATUS |
+                                          USER_RECORD_ALLOW_SIGNATURE |
+                                          USER_RECORD_STRIP_SECRET,
+
+        USER_RECORD_EXTRACT_SECRET      = USER_RECORD_REQUIRE_SECRET |
+                                          USER_RECORD_STRIP_REGULAR |
+                                          USER_RECORD_STRIP_PRIVILEGED |
+                                          USER_RECORD_STRIP_PER_MACHINE |
+                                          USER_RECORD_STRIP_BINDING |
+                                          USER_RECORD_STRIP_STATUS |
+                                          USER_RECORD_STRIP_SIGNATURE,
+
+        USER_RECORD_LOAD_SIGNABLE       = USER_RECORD_REQUIRE_REGULAR |
+                                          USER_RECORD_ALLOW_PRIVILEGED |
+                                          USER_RECORD_ALLOW_PER_MACHINE,
+
+        USER_RECORD_EXTRACT_SIGNABLE    = USER_RECORD_LOAD_SIGNABLE |
+                                          USER_RECORD_STRIP_SECRET |
+                                          USER_RECORD_STRIP_BINDING |
+                                          USER_RECORD_STRIP_STATUS |
+                                          USER_RECORD_STRIP_SIGNATURE,
+
+        USER_RECORD_LOAD_EMBEDDED       = USER_RECORD_REQUIRE_REGULAR |
+                                          USER_RECORD_ALLOW_PRIVILEGED |
+                                          USER_RECORD_ALLOW_PER_MACHINE |
+                                          USER_RECORD_ALLOW_SIGNATURE,
+
+        USER_RECORD_EXTRACT_EMBEDDED    = USER_RECORD_LOAD_EMBEDDED |
+                                          USER_RECORD_STRIP_SECRET |
+                                          USER_RECORD_STRIP_BINDING |
+                                          USER_RECORD_STRIP_STATUS,
+
+        /* Whether to log about loader errors beyond LOG_DEBUG */
+        USER_RECORD_LOG                 = 1U << 28,
+
+        /* Whether to ignore errors and load what we can */
+        USER_RECORD_PERMISSIVE          = 1U << 29,
+} UserRecordLoadFlags;
+
+static inline UserRecordLoadFlags USER_RECORD_REQUIRE(UserRecordMask m) {
+        assert((m & ~_USER_RECORD_MASK_MAX) == 0);
+        return m << 7;
+}
+
+static inline UserRecordLoadFlags USER_RECORD_ALLOW(UserRecordMask m) {
+        assert((m & ~_USER_RECORD_MASK_MAX) == 0);
+        return m << 14;
+}
+
+static inline UserRecordLoadFlags USER_RECORD_STRIP(UserRecordMask m) {
+        assert((m & ~_USER_RECORD_MASK_MAX) == 0);
+        return m << 21;
+}
+
+static inline UserRecordMask USER_RECORD_REQUIRE_MASK(UserRecordLoadFlags f) {
+        return (f >> 7) & _USER_RECORD_MASK_MAX;
+}
+
+static inline UserRecordMask USER_RECORD_ALLOW_MASK(UserRecordLoadFlags f) {
+        return ((f >> 14) & _USER_RECORD_MASK_MAX) | USER_RECORD_REQUIRE_MASK(f);
+}
+
+static inline UserRecordMask USER_RECORD_STRIP_MASK(UserRecordLoadFlags f) {
+        return (f >> 21) & _USER_RECORD_MASK_MAX;
+}
+
+static inline JsonDispatchFlags USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(UserRecordLoadFlags flags) {
+        return (FLAGS_SET(flags, USER_RECORD_LOG) ? JSON_LOG : 0) |
+                (FLAGS_SET(flags, USER_RECORD_PERMISSIVE) ? JSON_PERMISSIVE : 0);
+}
+
+typedef struct Pkcs11EncryptedKey {
+        /* The encrypted passphrase, which can be decrypted with the private key indicated below */
+        void *data;
+        size_t size;
+
+        /* Where to find the private key to decrypt the encrypted passphrase above */
+        char *uri;
+
+        /* What to test the decrypted passphrase against to allow access (classic UNIX password hash).  Note
+         * that the decrypted passphrase is also used for unlocking LUKS and fscrypt, and if the account is
+         * backed by LUKS or fscrypt the hashed password is only an additional layer of authentication, not
+         * the only. */
+        char *hashed_password;
+} Pkcs11EncryptedKey;
+
+typedef struct UserRecord {
+        /* The following three fields are not part of the JSON record */
+        unsigned n_ref;
+        UserRecordMask mask;
+        bool incomplete; /* incomplete due to security restrictions. */
+
+        char *user_name;
+        char *realm;
+        char *user_name_and_realm_auto; /* the user_name field concatenated with '@' and the realm, if the latter is defined */
+        char *real_name;
+        char *email_address;
+        char *password_hint;
+        char *icon_name;
+        char *location;
+
+        UserDisposition disposition;
+        uint64_t last_change_usec;
+        uint64_t last_password_change_usec;
+
+        char *shell;
+        mode_t umask;
+        char **environment;
+        char *time_zone;
+        char *preferred_language;
+        int nice_level;
+        struct rlimit *rlimits[_RLIMIT_MAX];
+
+        int locked;               /* prohibit activation in general */
+        uint64_t not_before_usec; /* prohibit activation before this unix time */
+        uint64_t not_after_usec;  /* prohibit activation after this unix time */
+
+        UserStorage storage;
+        uint64_t disk_size;
+        uint64_t disk_size_relative; /* Disk size, relative to the free bytes of the medium, normalized to UINT32_MAX = 100% */
+        char *skeleton_directory;
+        mode_t access_mode;
+
+        uint64_t tasks_max;
+        uint64_t memory_high;
+        uint64_t memory_max;
+        uint64_t cpu_weight;
+        uint64_t io_weight;
+
+        bool nosuid;
+        bool nodev;
+        bool noexec;
+
+        char **hashed_password;
+        char **ssh_authorized_keys;
+        char **password;
+        char **pkcs11_pin;
+
+        char *cifs_domain;
+        char *cifs_user_name;
+        char *cifs_service;
+
+        char *image_path;
+        char *image_path_auto; /* when none is configured explicitly, this is where we place the implicit image */
+        char *home_directory;
+        char *home_directory_auto; /* when none is set explicitly, this is where we place the implicit home directory */
+
+        uid_t uid;
+        gid_t gid;
+
+        char **member_of;
+
+        char *file_system_type;
+        sd_id128_t partition_uuid;
+        sd_id128_t luks_uuid;
+        sd_id128_t file_system_uuid;
+
+        int luks_discard;
+        char *luks_cipher;
+        char *luks_cipher_mode;
+        uint64_t luks_volume_key_size;
+        char *luks_pbkdf_hash_algorithm;
+        char *luks_pbkdf_type;
+        uint64_t luks_pbkdf_time_cost_usec;
+        uint64_t luks_pbkdf_memory_cost;
+        uint64_t luks_pbkdf_parallel_threads;
+
+        uint64_t disk_usage;
+        uint64_t disk_free;
+        uint64_t disk_ceiling;
+        uint64_t disk_floor;
+
+        char *state;
+        char *service;
+        int signed_locally;
+
+        uint64_t good_authentication_counter;
+        uint64_t bad_authentication_counter;
+        uint64_t last_good_authentication_usec;
+        uint64_t last_bad_authentication_usec;
+
+        uint64_t ratelimit_begin_usec;
+        uint64_t ratelimit_count;
+        uint64_t ratelimit_interval_usec;
+        uint64_t ratelimit_burst;
+
+        int removable;
+        int enforce_password_policy;
+        int auto_login;
+
+        uint64_t stop_delay_usec;   /* How long to leave systemd --user around on log-out */
+        int kill_processes;         /* Whether to kill user processes forcibly on log-out */
+
+        /* The following exist mostly so that we can cover the full /etc/shadow set of fields */
+        uint64_t password_change_min_usec;       /* maps to .sp_min */
+        uint64_t password_change_max_usec;       /* maps to .sp_max */
+        uint64_t password_change_warn_usec;      /* maps to .sp_warn */
+        uint64_t password_change_inactive_usec;  /* maps to .sp_inact */
+        int password_change_now;                 /* Require a password change immediately on next login (.sp_lstchg = 0) */
+
+        char **pkcs11_token_uri;
+        Pkcs11EncryptedKey *pkcs11_encrypted_key;
+        size_t n_pkcs11_encrypted_key;
+        int pkcs11_protected_authentication_path_permitted;
+
+        JsonVariant *json;
+} UserRecord;
+
+UserRecord* user_record_new(void);
+UserRecord* user_record_ref(UserRecord *h);
+UserRecord* user_record_unref(UserRecord *h);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(UserRecord*, user_record_unref);
+
+int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags flags);
+int user_record_build(UserRecord **ret, ...);
+
+const char *user_record_user_name_and_realm(UserRecord *h);
+UserStorage user_record_storage(UserRecord *h);
+const char *user_record_file_system_type(UserRecord *h);
+const char *user_record_skeleton_directory(UserRecord *h);
+mode_t user_record_access_mode(UserRecord *h);
+const char *user_record_home_directory(UserRecord *h);
+const char *user_record_image_path(UserRecord *h);
+unsigned long user_record_mount_flags(UserRecord *h);
+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);
+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);
+const char* user_record_luks_pbkdf_type(UserRecord *h);
+usec_t user_record_luks_pbkdf_time_cost_usec(UserRecord *h);
+uint64_t user_record_luks_pbkdf_memory_cost(UserRecord *h);
+uint64_t user_record_luks_pbkdf_parallel_threads(UserRecord *h);
+const char *user_record_luks_pbkdf_hash_algorithm(UserRecord *h);
+gid_t user_record_gid(UserRecord *h);
+UserDisposition user_record_disposition(UserRecord *h);
+int user_record_removable(UserRecord *h);
+usec_t user_record_ratelimit_interval_usec(UserRecord *h);
+uint64_t user_record_ratelimit_burst(UserRecord *h);
+bool user_record_can_authenticate(UserRecord *h);
+
+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);
+
+usec_t user_record_ratelimit_next_try(UserRecord *h);
+
+int user_record_clone(UserRecord *h, UserRecordLoadFlags flags, UserRecord **ret);
+int user_record_masked_equal(UserRecord *a, UserRecord *b, UserRecordMask mask);
+
+int user_record_test_blocked(UserRecord *h);
+int user_record_test_password_change_required(UserRecord *h);
+
+/* The following six are user by group-record.c, that's why we export them here */
+int json_dispatch_realm(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+int json_dispatch_user_group_list(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+int json_dispatch_user_disposition(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
+
+int per_machine_id_match(JsonVariant *ids, JsonDispatchFlags flags);
+int per_machine_hostname_match(JsonVariant *hns, JsonDispatchFlags flags);
+int user_group_record_mangle(JsonVariant *v, UserRecordLoadFlags load_flags, JsonVariant **ret_variant, UserRecordMask *ret_mask);
+
+const char* user_storage_to_string(UserStorage t) _const_;
+UserStorage user_storage_from_string(const char *s) _pure_;
+
+const char* user_disposition_to_string(UserDisposition t) _const_;
+UserDisposition user_disposition_from_string(const char *s) _pure_;
diff --git a/src/shared/userdb.c b/src/shared/userdb.c
new file mode 100644 (file)
index 0000000..92f8796
--- /dev/null
@@ -0,0 +1,1335 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/auxv.h>
+
+#include "dirent-util.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "group-record-nss.h"
+#include "missing_syscall.h"
+#include "parse-util.h"
+#include "set.h"
+#include "socket-util.h"
+#include "strv.h"
+#include "user-record-nss.h"
+#include "user-util.h"
+#include "userdb.h"
+#include "varlink.h"
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(link_hash_ops, void, trivial_hash_func, trivial_compare_func, Varlink, varlink_unref);
+
+typedef enum LookupWhat {
+        LOOKUP_USER,
+        LOOKUP_GROUP,
+        LOOKUP_MEMBERSHIP,
+        _LOOKUP_WHAT_MAX,
+} LookupWhat;
+
+struct UserDBIterator {
+        LookupWhat what;
+        Set *links;
+        bool nss_covered:1;
+        bool nss_iterating:1;
+        bool synthesize_root:1;
+        bool synthesize_nobody:1;
+        int error;
+        int nss_lock;
+        unsigned n_found;
+        sd_event *event;
+        UserRecord *found_user;                   /* when .what == LOOKUP_USER */
+        GroupRecord *found_group;                 /* when .what == LOOKUP_GROUP */
+
+        char *found_user_name, *found_group_name; /* when .what == LOOKUP_MEMBERSHIP */
+        char **members_of_group;
+        size_t index_members_of_group;
+        char *filter_user_name;
+};
+
+UserDBIterator* userdb_iterator_free(UserDBIterator *iterator) {
+        if (!iterator)
+                return NULL;
+
+        set_free(iterator->links);
+
+        switch (iterator->what) {
+
+        case LOOKUP_USER:
+                user_record_unref(iterator->found_user);
+
+                if (iterator->nss_iterating)
+                        endpwent();
+
+                break;
+
+        case LOOKUP_GROUP:
+                group_record_unref(iterator->found_group);
+
+                if (iterator->nss_iterating)
+                        endgrent();
+
+                break;
+
+        case LOOKUP_MEMBERSHIP:
+                free(iterator->found_user_name);
+                free(iterator->found_group_name);
+                strv_free(iterator->members_of_group);
+                free(iterator->filter_user_name);
+
+                if (iterator->nss_iterating)
+                        endgrent();
+
+                break;
+
+        default:
+                assert_not_reached("Unexpected state?");
+        }
+
+        sd_event_unref(iterator->event);
+        safe_close(iterator->nss_lock);
+
+        return mfree(iterator);
+}
+
+static UserDBIterator* userdb_iterator_new(LookupWhat what) {
+        UserDBIterator *i;
+
+        assert(what >= 0);
+        assert(what < _LOOKUP_WHAT_MAX);
+
+        i = new(UserDBIterator, 1);
+        if (!i)
+                return NULL;
+
+        *i = (UserDBIterator) {
+                .what = what,
+                .nss_lock = -1,
+        };
+
+        return i;
+}
+
+struct user_group_data {
+        JsonVariant *record;
+        bool incomplete;
+};
+
+static void user_group_data_release(struct user_group_data *d) {
+        json_variant_unref(d->record);
+}
+
+static int userdb_on_query_reply(
+                Varlink *link,
+                JsonVariant *parameters,
+                const char *error_id,
+                VarlinkReplyFlags flags,
+                void *userdata) {
+
+        UserDBIterator *iterator = userdata;
+        int r;
+
+        assert(iterator);
+
+        if (error_id) {
+                log_debug("Got lookup error: %s", error_id);
+
+                if (STR_IN_SET(error_id,
+                               "io.systemd.UserDatabase.NoRecordFound",
+                               "io.systemd.UserDatabase.ConflictingRecordFound"))
+                        r = -ESRCH;
+                else if (streq(error_id, "io.systemd.UserDatabase.ServiceNotAvailable"))
+                        r = -EHOSTDOWN;
+                else if (streq(error_id, VARLINK_ERROR_TIMEOUT))
+                        r = -ETIMEDOUT;
+                else
+                        r = -EIO;
+
+                goto finish;
+        }
+
+        switch (iterator->what) {
+
+        case LOOKUP_USER: {
+                _cleanup_(user_group_data_release) struct user_group_data user_data = {};
+
+                static const JsonDispatch dispatch_table[] = {
+                        { "record",     _JSON_VARIANT_TYPE_INVALID, json_dispatch_variant, offsetof(struct user_group_data, record),     0 },
+                        { "incomplete", JSON_VARIANT_BOOLEAN,       json_dispatch_boolean, offsetof(struct user_group_data, incomplete), 0 },
+                        {}
+                };
+                _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+
+                assert_se(!iterator->found_user);
+
+                r = json_dispatch(parameters, dispatch_table, NULL, 0, &user_data);
+                if (r < 0)
+                        goto finish;
+
+                if (!user_data.record) {
+                        r = log_debug_errno(SYNTHETIC_ERRNO(EIO), "Reply is missing record key");
+                        goto finish;
+                }
+
+                hr = user_record_new();
+                if (!hr) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                r = user_record_load(hr, user_data.record, USER_RECORD_LOAD_REFUSE_SECRET|USER_RECORD_PERMISSIVE);
+                if (r < 0)
+                        goto finish;
+
+                if (!hr->service) {
+                        r = log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "User record does not carry service information, refusing.");
+                        goto finish;
+                }
+
+                hr->incomplete = user_data.incomplete;
+
+                /* We match the root user by the name since the name is our primary key. We match the nobody
+                 * use by UID though, since the name might differ on OSes */
+                if (streq_ptr(hr->user_name, "root"))
+                        iterator->synthesize_root = false;
+                if (hr->uid == UID_NOBODY)
+                        iterator->synthesize_nobody = false;
+
+                iterator->found_user = TAKE_PTR(hr);
+                iterator->n_found++;
+
+                /* More stuff coming? then let's just exit cleanly here */
+                if (FLAGS_SET(flags, VARLINK_REPLY_CONTINUES))
+                        return 0;
+
+                /* Otherwise, let's remove this link and exit cleanly then */
+                r = 0;
+                goto finish;
+        }
+
+        case LOOKUP_GROUP: {
+                _cleanup_(user_group_data_release) struct user_group_data group_data = {};
+
+                static const JsonDispatch dispatch_table[] = {
+                        { "record",     _JSON_VARIANT_TYPE_INVALID, json_dispatch_variant, offsetof(struct user_group_data, record),     0 },
+                        { "incomplete", JSON_VARIANT_BOOLEAN,       json_dispatch_boolean, offsetof(struct user_group_data, incomplete), 0 },
+                        {}
+                };
+                _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
+
+                assert_se(!iterator->found_group);
+
+                r = json_dispatch(parameters, dispatch_table, NULL, 0, &group_data);
+                if (r < 0)
+                        goto finish;
+
+                if (!group_data.record) {
+                        r = log_debug_errno(SYNTHETIC_ERRNO(EIO), "Reply is missing record key");
+                        goto finish;
+                }
+
+                g = group_record_new();
+                if (!g) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                r = group_record_load(g, group_data.record, USER_RECORD_LOAD_REFUSE_SECRET|USER_RECORD_PERMISSIVE);
+                if (r < 0)
+                        goto finish;
+
+                if (!g->service) {
+                        r = log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Group record does not carry service information, refusing.");
+                        goto finish;
+                }
+
+                g->incomplete = group_data.incomplete;
+
+                if (streq_ptr(g->group_name, "root"))
+                        iterator->synthesize_root = false;
+                if (g->gid == GID_NOBODY)
+                        iterator->synthesize_nobody = false;
+
+                iterator->found_group = TAKE_PTR(g);
+                iterator->n_found++;
+
+                if (FLAGS_SET(flags, VARLINK_REPLY_CONTINUES))
+                        return 0;
+
+                r = 0;
+                goto finish;
+        }
+
+        case LOOKUP_MEMBERSHIP: {
+                struct membership_data {
+                        const char *user_name;
+                        const char *group_name;
+                } membership_data = {};
+
+                static const JsonDispatch dispatch_table[] = {
+                        { "userName",  JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(struct membership_data, user_name),  JSON_SAFE },
+                        { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(struct membership_data, group_name), JSON_SAFE },
+                        {}
+                };
+
+                assert(!iterator->found_user_name);
+                assert(!iterator->found_group_name);
+
+                r = json_dispatch(parameters, dispatch_table, NULL, 0, &membership_data);
+                if (r < 0)
+                        goto finish;
+
+                iterator->found_user_name = mfree(iterator->found_user_name);
+                iterator->found_group_name = mfree(iterator->found_group_name);
+
+                iterator->found_user_name = strdup(membership_data.user_name);
+                if (!iterator->found_user_name) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                iterator->found_group_name = strdup(membership_data.group_name);
+                if (!iterator->found_group_name) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                iterator->n_found++;
+
+                if (FLAGS_SET(flags, VARLINK_REPLY_CONTINUES))
+                        return 0;
+
+                r = 0;
+                goto finish;
+        }
+
+        default:
+                assert_not_reached("unexpected lookup");
+        }
+
+finish:
+        /* If we got one ESRCH, let that win. This way when we do a wild dump we won't be tripped up by bad
+         * errors if at least one connection ended cleanly */
+        if (r == -ESRCH || iterator->error == 0)
+                iterator->error = -r;
+
+        assert_se(set_remove(iterator->links, link) == link);
+        link = varlink_unref(link);
+        return 0;
+}
+
+static int userdb_connect(
+                UserDBIterator *iterator,
+                const char *path,
+                const char *method,
+                bool more,
+                JsonVariant *query) {
+
+        _cleanup_(varlink_unrefp) Varlink *vl = NULL;
+        int r;
+
+        assert(iterator);
+        assert(path);
+        assert(method);
+
+        r = varlink_connect_address(&vl, path);
+        if (r < 0)
+                return log_debug_errno(r, "Unable to connect to %s: %m", path);
+
+        varlink_set_userdata(vl, iterator);
+
+        if (!iterator->event) {
+                r = sd_event_new(&iterator->event);
+                if (r < 0)
+                        return log_debug_errno(r, "Unable to allocate event loop: %m");
+        }
+
+        r = varlink_attach_event(vl, iterator->event, SD_EVENT_PRIORITY_NORMAL);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to attach varlink connection to event loop: %m");
+
+        (void) varlink_set_description(vl, path);
+
+        r = varlink_bind_reply(vl, userdb_on_query_reply);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to bind reply callback: %m");
+
+        if (more)
+                r = varlink_observe(vl, method, query);
+        else
+                r = varlink_invoke(vl, method, query);
+        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);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to add varlink connection to set: %m");
+
+        TAKE_PTR(vl);
+        return r;
+}
+
+static int userdb_start_query(
+                UserDBIterator *iterator,
+                const char *method,
+                bool more,
+                JsonVariant *query,
+                UserDBFlags flags) {
+
+        _cleanup_(strv_freep) char **except = NULL, **only = NULL;
+        _cleanup_(closedirp) DIR *d = NULL;
+        struct dirent *de;
+        const char *e;
+        int r, ret = 0;
+
+        assert(iterator);
+        assert(method);
+
+        e = getenv("SYSTEMD_BYPASS_USERDB");
+        if (e) {
+                r = parse_boolean(e);
+                if (r > 0)
+                        return -ENOLINK;
+                if (r < 0) {
+                        except = strv_split(e, ":");
+                        if (!except)
+                                return -ENOMEM;
+                }
+        }
+
+        e = getenv("SYSTEMD_ONLY_USERDB");
+        if (e) {
+                only = strv_split(e, ":");
+                if (!only)
+                        return -ENOMEM;
+        }
+
+        /* First, let's talk to the multiplexer, if we can */
+        if ((flags & (USERDB_AVOID_MULTIPLEXER|USERDB_AVOID_DYNAMIC_USER|USERDB_AVOID_NSS|USERDB_DONT_SYNTHESIZE)) == 0 &&
+            !strv_contains(except, "io.systemd.Multiplexer") &&
+            (!only || strv_contains(only, "io.systemd.Multiplexer"))) {
+                _cleanup_(json_variant_unrefp) JsonVariant *patched_query = json_variant_ref(query);
+
+                r = json_variant_set_field_string(&patched_query, "service", "io.systemd.Multiplexer");
+                if (r < 0)
+                        return log_debug_errno(r, "Unable to set service JSON field: %m");
+
+                r = userdb_connect(iterator, "/run/systemd/userdb/io.systemd.Multiplexer", method, more, patched_query);
+                if (r >= 0) {
+                        iterator->nss_covered = true; /* The multiplexer does NSS */
+                        return 0;
+                }
+        }
+
+        d = opendir("/run/systemd/userdb/");
+        if (!d) {
+                if (errno == ENOENT)
+                        return -ESRCH;
+
+                return -errno;
+        }
+
+        FOREACH_DIRENT(de, d, return -errno) {
+                _cleanup_(json_variant_unrefp) JsonVariant *patched_query = NULL;
+                _cleanup_free_ char *p = NULL;
+                bool is_nss;
+
+                if (streq(de->d_name, "io.systemd.Multiplexer")) /* We already tried this above, don't try this again */
+                        continue;
+
+                if (FLAGS_SET(flags, USERDB_AVOID_DYNAMIC_USER) &&
+                    streq(de->d_name, "io.systemd.DynamicUser"))
+                        continue;
+
+                /* Avoid NSS is this is requested. Note that we also skip NSS when we were asked to skip the
+                 * multiplexer, since in that case it's safer to do NSS in the client side emulation below
+                 * (and when we run as part of systemd-userdbd.service we don't want to talk to ourselves
+                 * anyway). */
+                is_nss = streq(de->d_name, "io.systemd.NameServiceSwitch");
+                if ((flags & (USERDB_AVOID_NSS|USERDB_AVOID_MULTIPLEXER)) && is_nss)
+                        continue;
+
+                if (strv_contains(except, de->d_name))
+                        continue;
+
+                if (only && !strv_contains(only, de->d_name))
+                        continue;
+
+                p = path_join("/run/systemd/userdb/", de->d_name);
+                if (!p)
+                        return -ENOMEM;
+
+                patched_query = json_variant_ref(query);
+                r = json_variant_set_field_string(&patched_query, "service", de->d_name);
+                if (r < 0)
+                        return log_debug_errno(r, "Unable to set service JSON field: %m");
+
+                r = userdb_connect(iterator, p, method, more, patched_query);
+                if (is_nss && r >= 0) /* Turn off fallback NSS if we found the NSS service and could connect
+                                       * to it */
+                        iterator->nss_covered = true;
+
+                if (ret == 0 && r < 0)
+                        ret = r;
+        }
+
+        if (set_isempty(iterator->links))
+                return ret; /* propagate last error we saw if we couldn't connect to anything. */
+
+        /* We connected to some services, in this case, ignore the ones we failed on */
+        return 0;
+}
+
+static int userdb_process(
+                UserDBIterator *iterator,
+                UserRecord **ret_user_record,
+                GroupRecord **ret_group_record,
+                char **ret_user_name,
+                char **ret_group_name) {
+
+        int r;
+
+        assert(iterator);
+
+        for (;;) {
+                if (iterator->what == LOOKUP_USER && iterator->found_user) {
+                        if (ret_user_record)
+                                *ret_user_record = TAKE_PTR(iterator->found_user);
+                        else
+                                iterator->found_user = user_record_unref(iterator->found_user);
+
+                        if (ret_group_record)
+                                *ret_group_record = NULL;
+                        if (ret_user_name)
+                                *ret_user_name = NULL;
+                        if (ret_group_name)
+                                *ret_group_name = NULL;
+
+                        return 0;
+                }
+
+                if (iterator->what == LOOKUP_GROUP && iterator->found_group) {
+                        if (ret_group_record)
+                                *ret_group_record = TAKE_PTR(iterator->found_group);
+                        else
+                                iterator->found_group = group_record_unref(iterator->found_group);
+
+                        if (ret_user_record)
+                                *ret_user_record = NULL;
+                        if (ret_user_name)
+                                *ret_user_name = NULL;
+                        if (ret_group_name)
+                                *ret_group_name = NULL;
+
+                        return 0;
+                }
+
+                if (iterator->what == LOOKUP_MEMBERSHIP && iterator->found_user_name && iterator->found_group_name) {
+                        if (ret_user_name)
+                                *ret_user_name = TAKE_PTR(iterator->found_user_name);
+                        else
+                                iterator->found_user_name = mfree(iterator->found_user_name);
+
+                        if (ret_group_name)
+                                *ret_group_name = TAKE_PTR(iterator->found_group_name);
+                        else
+                                iterator->found_group_name = mfree(iterator->found_group_name);
+
+                        if (ret_user_record)
+                                *ret_user_record = NULL;
+                        if (ret_group_record)
+                                *ret_group_record = NULL;
+
+                        return 0;
+                }
+
+                if (set_isempty(iterator->links)) {
+                        if (iterator->error == 0)
+                                return -ESRCH;
+
+                        return -abs(iterator->error);
+                }
+
+                if (!iterator->event)
+                        return -ESRCH;
+
+                r = sd_event_run(iterator->event, UINT64_MAX);
+                if (r < 0)
+                        return r;
+        }
+}
+
+static int synthetic_root_user_build(UserRecord **ret) {
+        return user_record_build(
+                        ret,
+                        JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING("root")),
+                                          JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(0)),
+                                          JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(0)),
+                                          JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING("/root")),
+                                          JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
+}
+
+static int synthetic_nobody_user_build(UserRecord **ret) {
+        return user_record_build(
+                        ret,
+                        JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(NOBODY_USER_NAME)),
+                                          JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(UID_NOBODY)),
+                                          JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY)),
+                                          JSON_BUILD_PAIR("shell", JSON_BUILD_STRING(NOLOGIN)),
+                                          JSON_BUILD_PAIR("locked", JSON_BUILD_BOOLEAN(true)),
+                                          JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
+}
+
+int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret) {
+        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
+        int r;
+
+        if (!valid_user_group_name_compat(name))
+                return -EINVAL;
+
+        r = json_build(&query, JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name))));
+        if (r < 0)
+                return r;
+
+        iterator = userdb_iterator_new(LOOKUP_USER);
+        if (!iterator)
+                return -ENOMEM;
+
+        r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", false, query, flags);
+        if (r >= 0) {
+                r = userdb_process(iterator, ret, NULL, NULL, NULL);
+                if (r >= 0)
+                        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;
+
+                        /* Client-side NSS fallback */
+                        r = nss_user_record_by_name(name, ret);
+                        if (r >= 0)
+                                return r;
+                }
+        }
+
+        if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE)) {
+                if (streq(name, "root"))
+                        return synthetic_root_user_build(ret);
+
+                if (streq(name, NOBODY_USER_NAME) && synthesize_nobody())
+                        return synthetic_nobody_user_build(ret);
+        }
+
+        return r;
+}
+
+int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret) {
+        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
+        int r;
+
+        if (!uid_is_valid(uid))
+                return -EINVAL;
+
+        r = json_build(&query, JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid))));
+        if (r < 0)
+                return r;
+
+        iterator = userdb_iterator_new(LOOKUP_USER);
+        if (!iterator)
+                return -ENOMEM;
+
+        r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", false, query, flags);
+        if (r >= 0) {
+                r = userdb_process(iterator, ret, NULL, NULL, NULL);
+                if (r >= 0)
+                        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;
+
+                        /* Client-side NSS fallback */
+                        r = nss_user_record_by_uid(uid, ret);
+                        if (r >= 0)
+                                return r;
+                }
+        }
+
+        if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE)) {
+                if (uid == 0)
+                        return synthetic_root_user_build(ret);
+
+                if (uid == UID_NOBODY && synthesize_nobody())
+                        return synthetic_nobody_user_build(ret);
+        }
+
+        return r;
+}
+
+int userdb_all(UserDBFlags flags, UserDBIterator **ret) {
+        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+        int r;
+
+        assert(ret);
+
+        iterator = userdb_iterator_new(LOOKUP_USER);
+        if (!iterator)
+                return -ENOMEM;
+
+        iterator->synthesize_root = iterator->synthesize_nobody = !FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE);
+
+        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;
+
+                setpwent();
+                iterator->nss_iterating = true;
+        } else if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(iterator);
+        return 0;
+}
+
+int userdb_iterator_get(UserDBIterator *iterator, UserRecord **ret) {
+        int r;
+
+        assert(iterator);
+        assert(iterator->what == LOOKUP_USER);
+
+        if (iterator->nss_iterating) {
+                struct passwd *pw;
+
+                /* If NSS isn't covered elsewhere, let's iterate through it first, since it probably contains
+                 * the more traditional sources, which are probably good to show first. */
+
+                pw = getpwent();
+                if (pw) {
+                        _cleanup_free_ char *buffer = NULL;
+                        bool incomplete = false;
+                        struct spwd spwd;
+
+                        if (streq_ptr(pw->pw_name, "root"))
+                                iterator->synthesize_root = false;
+                        if (pw->pw_uid == UID_NOBODY)
+                                iterator->synthesize_nobody = false;
+
+                        r = nss_spwd_for_passwd(pw, &spwd, &buffer);
+                        if (r < 0) {
+                                log_debug_errno(r, "Failed to acquire shadow entry for user %s, ignoring: %m", pw->pw_name);
+                                incomplete = ERRNO_IS_PRIVILEGE(r);
+                        }
+
+                        r = nss_passwd_to_user_record(pw, r >= 0 ? &spwd : NULL, ret);
+                        if (r < 0)
+                                return r;
+
+                        if (ret)
+                                (*ret)->incomplete = incomplete;
+                        return r;
+                }
+
+                if (errno != 0)
+                        log_debug_errno(errno, "Failure to iterate NSS user database, ignoring: %m");
+
+                iterator->nss_iterating = false;
+                endpwent();
+        }
+
+        r = userdb_process(iterator, ret, NULL, NULL, NULL);
+
+        if (r < 0) {
+                if (iterator->synthesize_root) {
+                        iterator->synthesize_root = false;
+                        iterator->n_found++;
+                        return synthetic_root_user_build(ret);
+                }
+
+                if (iterator->synthesize_nobody) {
+                        iterator->synthesize_nobody = false;
+                        iterator->n_found++;
+                        return synthetic_nobody_user_build(ret);
+                }
+        }
+
+        /* if we found at least one entry, then ignore errors and indicate that we reached the end */
+        if (r < 0 && iterator->n_found > 0)
+                return -ESRCH;
+
+        return r;
+}
+
+static int synthetic_root_group_build(GroupRecord **ret) {
+        return group_record_build(
+                        ret,
+                        JSON_BUILD_OBJECT(JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING("root")),
+                                          JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(0)),
+                                          JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
+}
+
+static int synthetic_nobody_group_build(GroupRecord **ret) {
+        return group_record_build(
+                        ret,
+                        JSON_BUILD_OBJECT(JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(NOBODY_GROUP_NAME)),
+                                          JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY)),
+                                          JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("intrinsic"))));
+}
+
+int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret) {
+        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
+        int r;
+
+        if (!valid_user_group_name_compat(name))
+                return -EINVAL;
+
+        r = json_build(&query, JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name))));
+        if (r < 0)
+                return r;
+
+        iterator = userdb_iterator_new(LOOKUP_GROUP);
+        if (!iterator)
+                return -ENOMEM;
+
+        r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", false, query, flags);
+        if (r >= 0) {
+                r = userdb_process(iterator, NULL, ret, NULL, NULL);
+                if (r >= 0)
+                        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;
+
+                        r = nss_group_record_by_name(name, ret);
+                        if (r >= 0)
+                                return r;
+                }
+        }
+
+        if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE)) {
+                if (streq(name, "root"))
+                        return synthetic_root_group_build(ret);
+
+                if (streq(name, NOBODY_GROUP_NAME) && synthesize_nobody())
+                        return synthetic_nobody_group_build(ret);
+        }
+
+        return r;
+}
+
+int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret) {
+        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
+        int r;
+
+        if (!gid_is_valid(gid))
+                return -EINVAL;
+
+        r = json_build(&query, JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid))));
+        if (r < 0)
+                return r;
+
+        iterator = userdb_iterator_new(LOOKUP_GROUP);
+        if (!iterator)
+                return -ENOMEM;
+
+        r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", false, query, flags);
+        if (r >= 0) {
+                r = userdb_process(iterator, NULL, ret, NULL, NULL);
+                if (r >= 0)
+                        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;
+
+                        r = nss_group_record_by_gid(gid, ret);
+                        if (r >= 0)
+                                return r;
+                }
+        }
+
+        if (!FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE)) {
+                if (gid == 0)
+                        return synthetic_root_group_build(ret);
+
+                if (gid == GID_NOBODY && synthesize_nobody())
+                        return synthetic_nobody_group_build(ret);
+        }
+
+        return r;
+}
+
+int groupdb_all(UserDBFlags flags, UserDBIterator **ret) {
+        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+        int r;
+
+        assert(ret);
+
+        iterator = userdb_iterator_new(LOOKUP_GROUP);
+        if (!iterator)
+                return -ENOMEM;
+
+        iterator->synthesize_root = iterator->synthesize_nobody = !FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE);
+
+        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;
+
+                setgrent();
+                iterator->nss_iterating = true;
+        } if (r < 0)
+                  return r;
+
+        *ret = TAKE_PTR(iterator);
+        return 0;
+}
+
+int groupdb_iterator_get(UserDBIterator *iterator, GroupRecord **ret) {
+        int r;
+
+        assert(iterator);
+        assert(iterator->what == LOOKUP_GROUP);
+
+        if (iterator->nss_iterating) {
+                struct group *gr;
+
+                errno = 0;
+                gr = getgrent();
+                if (gr) {
+                        _cleanup_free_ char *buffer = NULL;
+                        bool incomplete = false;
+                        struct sgrp sgrp;
+
+                        if (streq_ptr(gr->gr_name, "root"))
+                                iterator->synthesize_root = false;
+                        if (gr->gr_gid == GID_NOBODY)
+                                iterator->synthesize_nobody = false;
+
+                        r = nss_sgrp_for_group(gr, &sgrp, &buffer);
+                        if (r < 0) {
+                                log_debug_errno(r, "Failed to acquire shadow entry for group %s, ignoring: %m", gr->gr_name);
+                                incomplete = ERRNO_IS_PRIVILEGE(r);
+                        }
+
+                        r = nss_group_to_group_record(gr, r >= 0 ? &sgrp : NULL, ret);
+                        if (r < 0)
+                                return r;
+
+                        if (ret)
+                                (*ret)->incomplete = incomplete;
+                        return r;
+                }
+
+                if (errno != 0)
+                        log_debug_errno(errno, "Failure to iterate NSS group database, ignoring: %m");
+
+                iterator->nss_iterating = false;
+                endgrent();
+        }
+
+        r = userdb_process(iterator, NULL, ret, NULL, NULL);
+        if (r < 0) {
+                if (iterator->synthesize_root) {
+                        iterator->synthesize_root = false;
+                        iterator->n_found++;
+                        return synthetic_root_group_build(ret);
+                }
+
+                if (iterator->synthesize_nobody) {
+                        iterator->synthesize_nobody = false;
+                        iterator->n_found++;
+                        return synthetic_nobody_group_build(ret);
+                }
+        }
+
+        /* if we found at least one entry, then ignore errors and indicate that we reached the end */
+        if (r < 0 && iterator->n_found > 0)
+                return -ESRCH;
+
+        return r;
+}
+
+int membershipdb_by_user(const char *name, UserDBFlags flags, UserDBIterator **ret) {
+        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
+        int r;
+
+        assert(ret);
+
+        if (!valid_user_group_name_compat(name))
+                return -EINVAL;
+
+        r = json_build(&query, JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(name))));
+        if (r < 0)
+                return r;
+
+        iterator = userdb_iterator_new(LOOKUP_MEMBERSHIP);
+        if (!iterator)
+                return -ENOMEM;
+
+        r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, query, flags);
+        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;
+
+        iterator->filter_user_name = strdup(name);
+        if (!iterator->filter_user_name)
+                return -ENOMEM;
+
+        setgrent();
+        iterator->nss_iterating = true;
+
+        r = 0;
+
+finish:
+        if (r >= 0)
+                *ret = TAKE_PTR(iterator);
+        return r;
+}
+
+int membershipdb_by_group(const char *name, UserDBFlags flags, UserDBIterator **ret) {
+        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
+        _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
+        int r;
+
+        assert(ret);
+
+        if (!valid_user_group_name_compat(name))
+                return -EINVAL;
+
+        r = json_build(&query, JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(name))));
+        if (r < 0)
+                return r;
+
+        iterator = userdb_iterator_new(LOOKUP_MEMBERSHIP);
+        if (!iterator)
+                return -ENOMEM;
+
+        r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, query, flags);
+        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;
+
+        /* 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);
+        if (gr) {
+                iterator->members_of_group = strv_copy(gr->members);
+                if (!iterator->members_of_group)
+                        return -ENOMEM;
+
+                iterator->index_members_of_group = 0;
+
+                iterator->found_group_name = strdup(name);
+                if (!iterator->found_group_name)
+                        return -ENOMEM;
+        }
+
+        r = 0;
+
+finish:
+        if (r >= 0)
+                *ret = TAKE_PTR(iterator);
+
+        return r;
+}
+
+int membershipdb_all(UserDBFlags flags, UserDBIterator **ret) {
+        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+        int r;
+
+        assert(ret);
+
+        iterator = userdb_iterator_new(LOOKUP_MEMBERSHIP);
+        if (!iterator)
+                return -ENOMEM;
+
+        r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, NULL, flags);
+        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;
+
+        setgrent();
+        iterator->nss_iterating = true;
+
+        r = 0;
+
+finish:
+        if (r >= 0)
+                *ret = TAKE_PTR(iterator);
+
+        return r;
+}
+
+int membershipdb_iterator_get(
+                UserDBIterator *iterator,
+                char **ret_user,
+                char **ret_group) {
+
+        int r;
+
+        assert(iterator);
+
+        for (;;) {
+                /* If we are iteratring through NSS acquire a new group entry if we haven't acquired one yet. */
+                if (!iterator->members_of_group) {
+                        struct group *g;
+
+                        if (!iterator->nss_iterating)
+                                break;
+
+                        assert(!iterator->found_user_name);
+                        do {
+                                errno = 0;
+                                g = getgrent();
+                                if (!g) {
+                                        if (errno != 0)
+                                                log_debug_errno(errno, "Failure during NSS group iteration, ignoring: %m");
+                                        break;
+                                }
+
+                        } while (iterator->filter_user_name ? !strv_contains(g->gr_mem, iterator->filter_user_name) :
+                                                              strv_isempty(g->gr_mem));
+
+                        if (g) {
+                                r = free_and_strdup(&iterator->found_group_name, g->gr_name);
+                                if (r < 0)
+                                        return r;
+
+                                if (iterator->filter_user_name)
+                                        iterator->members_of_group = strv_new(iterator->filter_user_name);
+                                else
+                                        iterator->members_of_group = strv_copy(g->gr_mem);
+                                if (!iterator->members_of_group)
+                                        return -ENOMEM;
+
+                                iterator->index_members_of_group = 0;
+                        } else {
+                                iterator->nss_iterating = false;
+                                endgrent();
+                                break;
+                        }
+                }
+
+                assert(iterator->found_group_name);
+                assert(iterator->members_of_group);
+                assert(!iterator->found_user_name);
+
+                if (iterator->members_of_group[iterator->index_members_of_group]) {
+                        _cleanup_free_ char *cu = NULL, *cg = NULL;
+
+                        if (ret_user) {
+                                cu = strdup(iterator->members_of_group[iterator->index_members_of_group]);
+                                if (!cu)
+                                        return -ENOMEM;
+                        }
+
+                        if (ret_group) {
+                                cg = strdup(iterator->found_group_name);
+                                if (!cg)
+                                        return -ENOMEM;
+                        }
+
+                        if (ret_user)
+                                *ret_user = TAKE_PTR(cu);
+
+                        if (ret_group)
+                                *ret_group = TAKE_PTR(cg);
+
+                        iterator->index_members_of_group++;
+                        return 0;
+                }
+
+                iterator->members_of_group = strv_free(iterator->members_of_group);
+                iterator->found_group_name = mfree(iterator->found_group_name);
+        }
+
+        r = userdb_process(iterator, NULL, NULL, ret_user, ret_group);
+        if (r < 0 && iterator->n_found > 0)
+                return -ESRCH;
+
+        return r;
+}
+
+int membershipdb_by_group_strv(const char *name, UserDBFlags flags, char ***ret) {
+        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+        _cleanup_strv_free_ char **members = NULL;
+        int r;
+
+        assert(name);
+        assert(ret);
+
+        r = membershipdb_by_group(name, flags, &iterator);
+        if (r < 0)
+                return r;
+
+        for (;;) {
+                _cleanup_free_ char *user_name = NULL;
+
+                r = membershipdb_iterator_get(iterator, &user_name, NULL);
+                if (r == -ESRCH)
+                        break;
+                if (r < 0)
+                        return r;
+
+                r = strv_consume(&members, TAKE_PTR(user_name));
+                if (r < 0)
+                        return r;
+        }
+
+        strv_sort(members);
+        strv_uniq(members);
+
+        *ret = TAKE_PTR(members);
+        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;
+
+        /* 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;
+
+                return -errno;
+        }
+
+        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;
+        }
+
+        return TAKE_FD(fd);
+}
diff --git a/src/shared/userdb.h b/src/shared/userdb.h
new file mode 100644 (file)
index 0000000..4288b0f
--- /dev/null
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "group-record.h"
+#include "user-record.h"
+
+/* Inquire local services for user/group records */
+
+typedef struct UserDBIterator UserDBIterator;
+
+UserDBIterator *userdb_iterator_free(UserDBIterator *iterator);
+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 */
+} UserDBFlags;
+
+int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret);
+int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret);
+int userdb_all(UserDBFlags flags, UserDBIterator **ret);
+int userdb_iterator_get(UserDBIterator *iterator, UserRecord **ret);
+
+int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret);
+int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret);
+int groupdb_all(UserDBFlags flags, UserDBIterator **ret);
+int groupdb_iterator_get(UserDBIterator *iterator, GroupRecord **ret);
+
+int membershipdb_by_user(const char *name, UserDBFlags flags, UserDBIterator **ret);
+int membershipdb_by_group(const char *name, UserDBFlags flags, UserDBIterator **ret);
+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);
index 77cea00cb9dad68f295bf3e0e92db2d7a69a5076..dff7d32535d5bc0dfba9ddc25ab4818c43e94610 100644 (file)
@@ -168,6 +168,7 @@ struct VarlinkServer {
 
         Hashmap *methods;
         VarlinkConnect connect_callback;
+        VarlinkDisconnect disconnect_callback;
 
         sd_event *event;
         int64_t event_priority;
@@ -270,6 +271,7 @@ static int varlink_new(Varlink **ret) {
 int varlink_connect_address(Varlink **ret, const char *address) {
         _cleanup_(varlink_unrefp) Varlink *v = NULL;
         union sockaddr_union sockaddr;
+        socklen_t sockaddr_len;
         int r;
 
         assert_return(ret, -EINVAL);
@@ -278,6 +280,7 @@ int varlink_connect_address(Varlink **ret, const char *address) {
         r = sockaddr_un_set_path(&sockaddr.un, address);
         if (r < 0)
                 return r;
+        sockaddr_len = r;
 
         r = varlink_new(&v);
         if (r < 0)
@@ -289,7 +292,7 @@ int varlink_connect_address(Varlink **ret, const char *address) {
 
         v->fd = fd_move_above_stdio(v->fd);
 
-        if (connect(v->fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0) {
+        if (connect(v->fd, &sockaddr.sa, sockaddr_len) < 0) {
                 if (!IN_SET(errno, EAGAIN, EINPROGRESS))
                         return -errno;
 
@@ -1146,6 +1149,7 @@ int varlink_flush(Varlink *v) {
 }
 
 static void varlink_detach_server(Varlink *v) {
+        VarlinkServer *saved_server;
         assert(v);
 
         if (!v->server)
@@ -1169,8 +1173,15 @@ static void varlink_detach_server(Varlink *v) {
         v->server->n_connections--;
 
         /* If this is a connection associated to a server, then let's disconnect the server and the
-         * connection from each other. This drops the dangling reference that connect_callback() set up. */
-        v->server = varlink_server_unref(v->server);
+         * connection from each other. This drops the dangling reference that connect_callback() set up. But
+         * before we release the references, let's call the disconnection callback if it is defined. */
+
+        saved_server = TAKE_PTR(v->server);
+
+        if (saved_server->disconnect_callback)
+                saved_server->disconnect_callback(saved_server, v, saved_server->userdata);
+
+        varlink_server_unref(saved_server);
         varlink_unref(v);
 }
 
@@ -2215,6 +2226,7 @@ int varlink_server_listen_fd(VarlinkServer *s, int fd) {
 
 int varlink_server_listen_address(VarlinkServer *s, const char *address, mode_t m) {
         union sockaddr_union sockaddr;
+        socklen_t sockaddr_len;
         _cleanup_close_ int fd = -1;
         int r;
 
@@ -2225,6 +2237,7 @@ int varlink_server_listen_address(VarlinkServer *s, const char *address, mode_t
         r = sockaddr_un_set_path(&sockaddr.un, address);
         if (r < 0)
                 return r;
+        sockaddr_len = r;
 
         fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
         if (fd < 0)
@@ -2235,7 +2248,7 @@ int varlink_server_listen_address(VarlinkServer *s, const char *address, mode_t
         (void) sockaddr_un_unlink(&sockaddr.un);
 
         RUN_WITH_UMASK(~m & 0777)
-                if (bind(fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0)
+                if (bind(fd, &sockaddr.sa, sockaddr_len) < 0)
                         return -errno;
 
         if (listen(fd, SOMAXCONN) < 0)
@@ -2413,6 +2426,16 @@ int varlink_server_bind_connect(VarlinkServer *s, VarlinkConnect callback) {
         return 0;
 }
 
+int varlink_server_bind_disconnect(VarlinkServer *s, VarlinkDisconnect callback) {
+        assert_return(s, -EINVAL);
+
+        if (callback && s->disconnect_callback && callback != s->disconnect_callback)
+                return -EBUSY;
+
+        s->disconnect_callback = callback;
+        return 0;
+}
+
 unsigned varlink_server_connections_max(VarlinkServer *s) {
         int dts;
 
@@ -2460,6 +2483,12 @@ int varlink_server_set_connections_max(VarlinkServer *s, unsigned m) {
         return 0;
 }
 
+unsigned varlink_server_current_connections(VarlinkServer *s) {
+        assert_return(s, UINT_MAX);
+
+        return s->n_connections;
+}
+
 int varlink_server_set_description(VarlinkServer *s, const char *description) {
         assert_return(s, -EINVAL);
 
index 0d9617d40352ff7aa6f6d790defb616fc1e9c1ef..7440f2ca44d711588c79138bb27f6a3357db3003 100644 (file)
@@ -51,6 +51,7 @@ typedef enum VarlinkServerFlags {
 typedef int (*VarlinkMethod)(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata);
 typedef int (*VarlinkReply)(Varlink *link, JsonVariant *parameters, const char *error_id, VarlinkReplyFlags flags, void *userdata);
 typedef int (*VarlinkConnect)(VarlinkServer *server, Varlink *link, void *userdata);
+typedef void (*VarlinkDisconnect)(VarlinkServer *server, Varlink *link, void *userdata);
 
 int varlink_connect_address(Varlink **ret, const char *address);
 int varlink_connect_fd(Varlink **ret, int fd);
@@ -134,6 +135,7 @@ int varlink_server_bind_method(VarlinkServer *s, const char *method, VarlinkMeth
 int varlink_server_bind_method_many_internal(VarlinkServer *s, ...);
 #define varlink_server_bind_method_many(s, ...) varlink_server_bind_method_many_internal(s, __VA_ARGS__, NULL)
 int varlink_server_bind_connect(VarlinkServer *s, VarlinkConnect connect);
+int varlink_server_bind_disconnect(VarlinkServer *s, VarlinkDisconnect disconnect);
 
 void* varlink_server_set_userdata(VarlinkServer *s, void *userdata);
 void* varlink_server_get_userdata(VarlinkServer *s);
@@ -150,6 +152,8 @@ unsigned varlink_server_connections_per_uid_max(VarlinkServer *s);
 int varlink_server_set_connections_per_uid_max(VarlinkServer *s, unsigned m);
 int varlink_server_set_connections_max(VarlinkServer *s, unsigned m);
 
+unsigned varlink_server_current_connections(VarlinkServer *s);
+
 int varlink_server_set_description(VarlinkServer *s, const char *description);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(Varlink *, varlink_unref);
index cfd254ad556ed31261768c8098a0fb1808326b1c..fbfddc0262fc161938a4868341ac3d6da4629d1f 100644 (file)
 #include "sd-messages.h"
 
 #include "btrfs-util.h"
+#include "bus-error.h"
 #include "def.h"
 #include "exec-util.h"
 #include "fd-util.h"
-#include "format-util.h"
 #include "fileio.h"
+#include "format-util.h"
 #include "log.h"
 #include "main-func.h"
 #include "parse-util.h"
@@ -125,6 +126,49 @@ static int write_state(FILE **f, char **states) {
         return r;
 }
 
+static int lock_all_homes(void) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        int r;
+
+        /* Let's synchronously lock all home directories managed by homed that have been marked for it. This
+         * way the key material required to access these volumes is hopefully removed from memory. */
+
+        r = sd_bus_open_system(&bus);
+        if (r < 0)
+                return log_warning_errno(r, "Failed to connect to system bus, ignoring: %m");
+
+        r = sd_bus_message_new_method_call(
+                        bus,
+                        &m,
+                        "org.freedesktop.home1",
+                        "/org/freedesktop/home1",
+                        "org.freedesktop.home1.Manager",
+                        "LockAllHomes");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        /* If homed is not running it can't have any home directories active either. */
+        r = sd_bus_message_set_auto_start(m, false);
+        if (r < 0)
+                return log_error_errno(r, "Failed to disable auto-start of LockAllHomes() message: %m");
+
+        r = sd_bus_call(bus, m, DEFAULT_TIMEOUT_USEC, &error, NULL);
+        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)) {
+                        log_debug("systemd-homed is not running, skipping locking of home directories.");
+                        return 0;
+                }
+
+                return log_error_errno(r, "Failed to lock home directories: %s", bus_error_message(&error, r));
+        }
+
+        log_debug("Successfully requested for all home directories to be locked.");
+        return 0;
+}
+
 static int execute(char **modes, char **states) {
         char *arguments[] = {
                 NULL,
@@ -166,6 +210,7 @@ static int execute(char **modes, char **states) {
         }
 
         (void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
+        (void) lock_all_homes();
 
         log_struct(LOG_INFO,
                    "MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR,
index 2fb9c854fa504ba000b3b4073efeba5703243207..2ee6fc2f0a6aa0fcf4fbf2539cc4ba475875262b 100644 (file)
@@ -373,20 +373,21 @@ static int resolve_remote(Connection *c) {
                 .ai_flags = AI_ADDRCONFIG
         };
 
-        union sockaddr_union sa = {};
         const char *node, *service;
         int r;
 
         if (IN_SET(arg_remote_host[0], '/', '@')) {
-                int salen;
+                union sockaddr_union sa;
+                int sa_len;
 
-                salen = sockaddr_un_set_path(&sa.un, arg_remote_host);
-                if (salen < 0) {
-                        log_error_errno(salen, "Specified address doesn't fit in an AF_UNIX address, refusing: %m");
+                r = sockaddr_un_set_path(&sa.un, arg_remote_host);
+                if (r < 0) {
+                        log_error_errno(r, "Specified address doesn't fit in an AF_UNIX address, refusing: %m");
                         goto fail;
                 }
+                sa_len = r;
 
-                return connection_start(c, &sa.sa, salen);
+                return connection_start(c, &sa.sa, sa_len);
         }
 
         service = strrchr(arg_remote_host, ':');
index 56d86d8eba31e7d382f21614db4724206c14743b..0cdb740d21891c552ebeaa226f108cd3cd14b695 100644 (file)
 
 #include "conf-files.h"
 #include "def.h"
+#include "errno-util.h"
 #include "fd-util.h"
 #include "fileio.h"
+#include "glob-util.h"
 #include "hashmap.h"
 #include "log.h"
 #include "main-func.h"
@@ -48,6 +50,26 @@ static Option *option_free(Option *o) {
 DEFINE_TRIVIAL_CLEANUP_FUNC(Option*, option_free);
 DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(option_hash_ops, char, string_hash_func, string_compare_func, Option, option_free);
 
+static bool test_prefix(const char *p) {
+        char **i;
+
+        if (strv_isempty(arg_prefixes))
+                return true;
+
+        STRV_FOREACH(i, arg_prefixes) {
+                const char *t;
+
+                t = path_startswith(*i, "/proc/sys/");
+                if (!t)
+                        t = *i;
+
+                if (path_startswith(p, t))
+                        return true;
+        }
+
+        return false;
+}
+
 static Option *option_new(
                 const char *key,
                 const char *value,
@@ -56,7 +78,6 @@ static Option *option_new(
         _cleanup_(option_freep) Option *o = NULL;
 
         assert(key);
-        assert(value);
 
         o = new(Option, 1);
         if (!o)
@@ -64,16 +85,38 @@ static Option *option_new(
 
         *o = (Option) {
                 .key = strdup(key),
-                .value = strdup(value),
+                .value = value ? strdup(value) : NULL,
                 .ignore_failure = ignore_failure,
         };
 
-        if (!o->key || !o->value)
+        if (!o->key)
+                return NULL;
+        if (value && !o->value)
                 return NULL;
 
         return TAKE_PTR(o);
 }
 
+static int sysctl_write_or_warn(const char *key, const char *value, bool ignore_failure) {
+        int r;
+
+        r = sysctl_write(key, value);
+        if (r < 0) {
+                /* If the sysctl is not available in the kernel or we are running with reduced privileges and
+                 * cannot write it, then log about the issue, and proceed without failing. (EROFS is treated
+                 * as a permission problem here, since that's how container managers usually protected their
+                 * sysctls.) In all other cases log an error and make the tool fail. */
+                if (ignore_failure || r == -EROFS || ERRNO_IS_PRIVILEGE(r))
+                        log_debug_errno(r, "Couldn't write '%s' to '%s', ignoring: %m", value, key);
+                else if (r == -ENOENT)
+                        log_info_errno(r, "Couldn't write '%s' to '%s', ignoring: %m", value, key);
+                else
+                        return log_error_errno(r, "Couldn't write '%s' to '%s': %m", value, key);
+        }
+
+        return 0;
+}
+
 static int apply_all(OrderedHashmap *sysctl_options) {
         Option *option;
         Iterator i;
@@ -82,47 +125,63 @@ static int apply_all(OrderedHashmap *sysctl_options) {
         ORDERED_HASHMAP_FOREACH(option, sysctl_options, i) {
                 int k;
 
-                k = sysctl_write(option->key, option->value);
-                if (k < 0) {
-                        /* If the sysctl is not available in the kernel or we are running with reduced
-                         * privileges and cannot write it, then log about the issue at LOG_NOTICE level, and
-                         * proceed without failing. (EROFS is treated as a permission problem here, since
-                         * that's how container managers usually protected their sysctls.) In all other cases
-                         * log an error and make the tool fail. */
+                /* Ignore "negative match" options, they are there only to exclude stuff from globs. */
+                if (!option->value)
+                        continue;
 
-                        if (IN_SET(k, -EPERM, -EACCES, -EROFS, -ENOENT) || option->ignore_failure)
-                                log_notice_errno(k, "Couldn't write '%s' to '%s', ignoring: %m", option->value, option->key);
-                        else {
-                                log_error_errno(k, "Couldn't write '%s' to '%s': %m", option->value, option->key);
-                                if (r == 0)
-                                        r = k;
-                        }
-                }
-        }
+                if (string_is_glob(option->key)) {
+                        _cleanup_strv_free_ char **paths = NULL;
+                        _cleanup_free_ char *pattern = NULL;
+                        char **s;
 
-        return r;
-}
+                        pattern = path_join("/proc/sys", option->key);
+                        if (!pattern)
+                                return log_oom();
 
-static bool test_prefix(const char *p) {
-        char **i;
+                        k = glob_extend(&paths, pattern);
+                        if (k < 0) {
+                                if (option->ignore_failure || ERRNO_IS_PRIVILEGE(r))
+                                        log_debug_errno(k, "Failed to resolve glob '%s', ignoring: %m",
+                                                        option->key);
+                                else {
+                                        log_error_errno(k, "Couldn't resolve glob '%s': %m",
+                                                        option->key);
+                                        if (r == 0)
+                                                r = k;
+                                }
 
-        if (strv_isempty(arg_prefixes))
-                return true;
+                        } else if (strv_isempty(paths))
+                                log_debug("No match for glob: %s", option->key);
 
-        STRV_FOREACH(i, arg_prefixes) {
-                const char *t;
+                        STRV_FOREACH(s, paths) {
+                                const char *key;
 
-                t = path_startswith(*i, "/proc/sys/");
-                if (!t)
-                        t = *i;
-                if (path_startswith(p, t))
-                        return true;
+                                assert_se(key = path_startswith(*s, "/proc/sys"));
+
+                                if (!test_prefix(key))
+                                        continue;
+
+                                if (ordered_hashmap_contains(sysctl_options, key)) {
+                                        log_info("Not setting %s (explicit setting exists).", key);
+                                        continue;
+                                }
+
+                                k = sysctl_write_or_warn(key, option->value, option->ignore_failure);
+                                if (r == 0)
+                                        r = k;
+                        }
+
+                } else {
+                        k = sysctl_write_or_warn(option->key, option->value, option->ignore_failure);
+                        if (r == 0)
+                                r = k;
+                }
         }
 
-        return false;
+        return r;
 }
 
-static int parse_file(OrderedHashmap *sysctl_options, const char *path, bool ignore_enoent) {
+static int parse_file(OrderedHashmap **sysctl_options, const char *path, bool ignore_enoent) {
         _cleanup_fclose_ FILE *f = NULL;
         unsigned c = 0;
         int r;
@@ -141,7 +200,7 @@ static int parse_file(OrderedHashmap *sysctl_options, const char *path, bool ign
         for (;;) {
                 _cleanup_(option_freep) Option *new_option = NULL;
                 _cleanup_free_ char *l = NULL;
-                bool ignore_failure;
+                bool ignore_failure = false;
                 Option *existing;
                 char *p, *value;
                 int k;
@@ -162,43 +221,56 @@ static int parse_file(OrderedHashmap *sysctl_options, const char *path, bool ign
                         continue;
 
                 value = strchr(p, '=');
-                if (!value) {
-                        log_syntax(NULL, LOG_WARNING, path, c, 0, "Line is not an assignment, ignoring: %s", p);
-                        if (r == 0)
-                                r = -EINVAL;
-                        continue;
-                }
+                if (value) {
+                        if (p[0] == '-') {
+                                ignore_failure = true;
+                                p++;
+                        }
 
-                *value = 0;
-                value++;
+                        *value = 0;
+                        value++;
+                        value = strstrip(value);
 
-                p = strstrip(p);
-                ignore_failure = p[0] == '-';
-                if (ignore_failure)
-                        p++;
+                } else {
+                        if (p[0] == '-')
+                                /* We have a "negative match" option. Let's continue with value==NULL. */
+                                p++;
+                        else {
+                                log_syntax(NULL, LOG_WARNING, path, c, 0,
+                                           "Line is not an assignment, ignoring: %s", p);
+                                if (r == 0)
+                                        r = -EINVAL;
+                                continue;
+                        }
+                }
 
+                p = strstrip(p);
                 p = sysctl_normalize(p);
-                value = strstrip(value);
 
-                if (!test_prefix(p))
+                /* We can't filter out globs at this point, we'll need to do that later. */
+                if (!string_is_glob(p) &&
+                    !test_prefix(p))
                         continue;
 
-                existing = ordered_hashmap_get(sysctl_options, p);
+                if (ordered_hashmap_ensure_allocated(sysctl_options, &option_hash_ops) < 0)
+                        return log_oom();
+
+                existing = ordered_hashmap_get(*sysctl_options, p);
                 if (existing) {
-                        if (streq(value, existing->value)) {
+                        if (streq_ptr(value, existing->value)) {
                                 existing->ignore_failure = existing->ignore_failure || ignore_failure;
                                 continue;
                         }
 
                         log_debug("Overwriting earlier assignment of %s at '%s:%u'.", p, path, c);
-                        option_free(ordered_hashmap_remove(sysctl_options, p));
+                        option_free(ordered_hashmap_remove(*sysctl_options, p));
                 }
 
                 new_option = option_new(p, value, ignore_failure);
                 if (!new_option)
                         return log_oom();
 
-                k = ordered_hashmap_put(sysctl_options, new_option->key, new_option);
+                k = ordered_hashmap_put(*sysctl_options, new_option->key, new_option);
                 if (k < 0)
                         return log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", p);
 
@@ -320,17 +392,13 @@ static int run(int argc, char *argv[]) {
 
         umask(0022);
 
-        sysctl_options = ordered_hashmap_new(&option_hash_ops);
-        if (!sysctl_options)
-                return log_oom();
-
         if (argc > optind) {
                 int i;
 
                 r = 0;
 
                 for (i = optind; i < argc; i++) {
-                        k = parse_file(sysctl_options, argv[i], false);
+                        k = parse_file(&sysctl_options, argv[i], false);
                         if (k < 0 && r == 0)
                                 r = k;
                 }
@@ -349,7 +417,7 @@ static int run(int argc, char *argv[]) {
                 }
 
                 STRV_FOREACH(f, files) {
-                        k = parse_file(sysctl_options, *f, true);
+                        k = parse_file(&sysctl_options, *f, true);
                         if (k < 0 && r == 0)
                                 r = k;
                 }
index 64c1401afc254fa982e12b87ef6efbbae61178cf..146e2263f3b5c6fe1b53fb834db3ff8b6ebbd936 100644 (file)
@@ -39,6 +39,7 @@
 #include "exec-util.h"
 #include "exit-status.h"
 #include "fd-util.h"
+#include "format-table.h"
 #include "format-util.h"
 #include "fs-util.h"
 #include "glob-util.h"
@@ -389,122 +390,42 @@ static bool output_show_unit(const UnitInfo *u, char **patterns) {
 }
 
 static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
-        unsigned circle_len = 0, id_len, max_id_len, load_len, active_len, sub_len, job_len, desc_len, max_desc_len;
+        _cleanup_(table_unrefp) Table *table = NULL;
         const UnitInfo *u;
-        unsigned n_shown = 0;
         int job_count = 0;
-        bool full = arg_full || FLAGS_SET(arg_pager_flags, PAGER_DISABLE);
-
-        max_id_len = STRLEN("UNIT");
-        load_len = STRLEN("LOAD");
-        active_len = STRLEN("ACTIVE");
-        sub_len = STRLEN("SUB");
-        job_len = STRLEN("JOB");
-        max_desc_len = STRLEN("DESCRIPTION");
-
-        for (u = unit_infos; u < unit_infos + c; u++) {
-                max_id_len = MAX(max_id_len, strlen(u->id) + (u->machine ? strlen(u->machine)+1 : 0));
-                load_len = MAX(load_len, strlen(u->load_state));
-                active_len = MAX(active_len, strlen(u->active_state));
-                sub_len = MAX(sub_len, strlen(u->sub_state));
-                max_desc_len = MAX(max_desc_len, strlen(u->description));
-
-                if (u->job_id != 0) {
-                        job_len = MAX(job_len, strlen(u->job_type));
-                        job_count++;
-                }
-
-                if (!arg_no_legend &&
-                    (streq(u->active_state, "failed") ||
-                     STR_IN_SET(u->load_state, "error", "not-found", "bad-setting", "masked")))
-                        circle_len = 2;
-        }
-
-        if (!arg_full && original_stdout_is_tty) {
-                unsigned basic_len;
-
-                id_len = MIN(max_id_len, 25u); /* as much as it needs, but at most 25 for now */
-                basic_len = circle_len + 1 + id_len + 1 + load_len + 1 + active_len + 1 + sub_len + 1;
-
-                if (job_count)
-                        basic_len += job_len + 1;
-
-                if (basic_len < (unsigned) columns()) {
-                        unsigned extra_len, incr;
-                        extra_len = columns() - basic_len;
+        int r;
 
-                        /* Either UNIT already got 25, or is fully satisfied.
-                         * Grant up to 25 to DESC now. */
-                        incr = MIN(extra_len, 25u);
-                        desc_len = incr;
-                        extra_len -= incr;
+        table = table_new("", "unit", "load", "active", "sub", "job", "description");
+        if (!table)
+                return log_oom();
 
-                        /* Of the remainder give as much as the ID needs to the ID, and give the rest to the
-                         * description but not more than it needs. */
-                        if (extra_len > 0) {
-                                incr = MIN(max_id_len - id_len, extra_len);
-                                id_len += incr;
-                                desc_len += MIN(extra_len - incr, max_desc_len - desc_len);
-                        }
-                } else
-                        desc_len = 0;
-        } else {
-                id_len = max_id_len;
-                desc_len = max_desc_len;
-        }
+        table_set_header(table, !arg_no_legend);
+        if (arg_full)
+                table_set_width(table, 0);
 
         for (u = unit_infos; u < unit_infos + c; u++) {
-                _cleanup_free_ char *e = NULL, *j = NULL;
-                const char *on_underline = "", *off_underline = "";
-                const char *on_loaded = "", *off_loaded = "";
-                const char *on_active = "", *off_active = "";
-                const char *on_circle = "", *off_circle = "";
-                const char *id;
+                _cleanup_free_ char *j = NULL;
+                const char *on_underline = "", *on_loaded = "", *on_active = "";
+                const char *on_circle = "", *id;
                 bool circle = false, underline = false;
 
-                if (!n_shown && !arg_no_legend) {
-
-                        if (circle_len > 0)
-                                fputs("  ", stdout);
-
-                        printf("%s%-*s %-*s %-*s %-*s ",
-                               ansi_underline(),
-                               id_len, "UNIT",
-                               load_len, "LOAD",
-                               active_len, "ACTIVE",
-                               sub_len, "SUB");
-
-                        if (job_count)
-                                printf("%-*s ", job_len, "JOB");
-
-                        printf("%-*.*s%s\n",
-                               desc_len,
-                               full ? -1 : (int) desc_len,
-                               "DESCRIPTION",
-                               ansi_normal());
-                }
-
-                n_shown++;
-
                 if (u + 1 < unit_infos + c &&
                     !streq(unit_type_suffix(u->id), unit_type_suffix((u + 1)->id))) {
                         on_underline = ansi_underline();
-                        off_underline = ansi_normal();
                         underline = true;
                 }
 
                 if (STR_IN_SET(u->load_state, "error", "not-found", "bad-setting", "masked") && !arg_plain) {
                         on_circle = ansi_highlight_yellow();
-                        off_circle = ansi_normal();
                         circle = true;
                         on_loaded = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
-                        off_loaded = underline ? on_underline : ansi_normal();
                 } else if (streq(u->active_state, "failed") && !arg_plain) {
                         on_circle = ansi_highlight_red();
-                        off_circle = ansi_normal();
                         circle = true;
                         on_active = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
-                        off_active = underline ? on_underline : ansi_normal();
+                } else {
+                        on_active = on_underline;
+                        on_loaded = on_underline;
                 }
 
                 if (u->machine) {
@@ -516,36 +437,47 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
                 } else
                         id = u->id;
 
-                if (arg_full) {
-                        e = ellipsize(id, id_len, 33);
-                        if (!e)
-                                return log_oom();
-
-                        id = e;
-                }
-
-                if (circle_len > 0)
-                        printf("%s%s%s ", on_circle, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ", off_circle);
+                r = table_add_many(table,
+                                   TABLE_STRING, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ",
+                                   TABLE_SET_COLOR, on_circle,
+                                   TABLE_STRING, id,
+                                   TABLE_SET_COLOR, on_active,
+                                   TABLE_STRING, u->load_state,
+                                   TABLE_SET_COLOR, on_loaded,
+                                   TABLE_STRING, u->active_state,
+                                   TABLE_SET_COLOR, on_active,
+                                   TABLE_STRING, u->sub_state,
+                                   TABLE_SET_COLOR, on_active,
+                                   TABLE_STRING, u->job_id ? u->job_type: "",
+                                   TABLE_SET_COLOR, u->job_id ? on_underline : "",
+                                   TABLE_STRING, u->description,
+                                   TABLE_SET_COLOR, on_underline);
+                if (r < 0)
+                        return table_log_add_error(r);
 
-                printf("%s%s%-*s%s %s%-*s%s %s%-*s %-*s%s %-*s",
-                       on_underline,
-                       on_active, id_len, id, off_active,
-                       on_loaded, load_len, u->load_state, off_loaded,
-                       on_active, active_len, u->active_state,
-                       sub_len, u->sub_state, off_active,
-                       job_count ? job_len + 1 : 0, u->job_id ? u->job_type : "");
+                if (u->job_id != 0)
+                        job_count++;
+        }
 
-                printf("%-*.*s%s\n",
-                       desc_len,
-                       full ? -1 : (int) desc_len,
-                       u->description,
-                       off_underline);
+        if (job_count == 0) {
+                /* There's no data in the JOB column, so let's hide it */
+                /* Also, convert all number constants to size_t so va_arg()
+                 * in table_set_display() fetches a correct number of bytes from
+                 * the stack */
+                r = table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4, (size_t) 6, (size_t) -1);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set columns to display: %m");
         }
 
+        r = table_print(table, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to print the table: %m");
+
         if (!arg_no_legend) {
                 const char *on, *off;
+                size_t records = table_get_rows(table) - 1;
 
-                if (n_shown) {
+                if (records > 0) {
                         puts("\n"
                              "LOAD   = Reflects whether the unit definition was properly loaded.\n"
                              "ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n"
@@ -559,15 +491,15 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
                 }
 
                 if (arg_all || strv_contains(arg_states, "inactive"))
-                        printf("%s%u loaded units listed.%s\n"
+                        printf("%s%zu loaded units listed.%s\n"
                                "To show all installed unit files use 'systemctl list-unit-files'.\n",
-                               on, n_shown, off);
+                               on, records, off);
                 else if (!arg_states)
-                        printf("%s%u loaded units listed.%s Pass --all to see loaded but inactive units, too.\n"
+                        printf("%s%zu loaded units listed.%s Pass --all to see loaded but inactive units, too.\n"
                                "To show all installed unit files use 'systemctl list-unit-files'.\n",
-                               on, n_shown, off);
+                               on, records, off);
                 else
-                        printf("%u loaded units listed.\n", n_shown);
+                        printf("%zu loaded units listed.\n", records);
         }
 
         return 0;
@@ -853,6 +785,8 @@ static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, cha
         if (r < 0)
                 return log_error_errno(r, "Failed to get properties of %s: %s", name, bus_error_message(&error, r));
 
+        strv_uniq(deps); /* Sometimes a unit might have multiple deps on the other unit,
+                          * but we still want to show it just once. */
         *ret = TAKE_PTR(deps);
 
         return 0;
@@ -1048,39 +982,30 @@ static int socket_info_compare(const struct socket_info *a, const struct socket_
 }
 
 static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
+        _cleanup_(table_unrefp) Table *table = NULL;
         struct socket_info *s;
-        unsigned pathlen = STRLEN("LISTEN"),
-                typelen = STRLEN("TYPE") * arg_show_types,
-                socklen = STRLEN("UNIT"),
-                servlen = STRLEN("ACTIVATES");
         const char *on, *off;
+        int r;
 
-        for (s = socket_infos; s < socket_infos + cs; s++) {
-                unsigned tmp = 0;
-                char **a;
-
-                socklen = MAX(socklen, strlen(s->id));
-                if (arg_show_types)
-                        typelen = MAX(typelen, strlen(s->type));
-                pathlen = MAX(pathlen, strlen(s->path) + (s->machine ? strlen(s->machine)+1 : 0));
+        table = table_new("listen", "type", "units", "activates");
+        if (!table)
+                return log_oom();
 
-                STRV_FOREACH(a, s->triggered)
-                        tmp += strlen(*a) + 2*(a != s->triggered);
-                servlen = MAX(servlen, tmp);
+        if (!arg_show_types) {
+                /* Hide the second (TYPE) column */
+                r = table_set_display(table, (size_t) 0, (size_t) 2, (size_t) 3, (size_t) -1);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set columns to display: %m");
         }
 
-        if (cs) {
-                if (!arg_no_legend)
-                        printf("%-*s %-*.*s%-*s %s\n",
-                               pathlen, "LISTEN",
-                               typelen + arg_show_types, typelen + arg_show_types, "TYPE ",
-                               socklen, "UNIT",
-                               "ACTIVATES");
+        table_set_header(table, !arg_no_legend);
+        if (arg_full)
+                table_set_width(table, 0);
 
+        if (cs) {
                 for (s = socket_infos; s < socket_infos + cs; s++) {
-                        _cleanup_free_ char *j = NULL;
+                        _cleanup_free_ char *j = NULL, *activates = NULL;
                         const char *path;
-                        char **a;
 
                         if (s->machine) {
                                 j = strjoin(s->machine, ":", s->path);
@@ -1090,29 +1015,32 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
                         } else
                                 path = s->path;
 
-                        if (arg_show_types)
-                                printf("%-*s %-*s %-*s",
-                                       pathlen, path, typelen, s->type, socklen, s->id);
-                        else
-                                printf("%-*s %-*s",
-                                       pathlen, path, socklen, s->id);
-                        STRV_FOREACH(a, s->triggered)
-                                printf("%s %s",
-                                       a == s->triggered ? "" : ",", *a);
-                        printf("\n");
+                        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);
+                        if (r < 0)
+                                return table_log_add_error(r);
                 }
 
                 on = ansi_highlight();
                 off = ansi_normal();
-                if (!arg_no_legend)
-                        printf("\n");
         } else {
                 on = ansi_highlight_red();
                 off = ansi_normal();
         }
 
+        r = table_print(table, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to print the table: %m");
+
         if (!arg_no_legend) {
-                printf("%s%u sockets listed.%s\n", on, cs, off);
+                printf("\n%s%u sockets listed.%s\n", on, cs, off);
                 if (!arg_all)
                         printf("Pass --all to see loaded but inactive sockets, too.\n");
         }
@@ -1303,73 +1231,25 @@ static int timer_info_compare(const struct timer_info *a, const struct timer_inf
 }
 
 static int output_timers_list(struct timer_info *timer_infos, unsigned n) {
+        _cleanup_(table_unrefp) Table *table = NULL;
         struct timer_info *t;
-        unsigned
-                nextlen = STRLEN("NEXT"),
-                leftlen = STRLEN("LEFT"),
-                lastlen = STRLEN("LAST"),
-                passedlen = STRLEN("PASSED"),
-                unitlen = STRLEN("UNIT"),
-                activatelen = STRLEN("ACTIVATES");
-
         const char *on, *off;
+        int r;
 
         assert(timer_infos || n == 0);
 
-        for (t = timer_infos; t < timer_infos + n; t++) {
-                unsigned ul = 0;
-                char **a;
-
-                if (t->next_elapse > 0) {
-                        char tstamp[FORMAT_TIMESTAMP_MAX] = "", trel[FORMAT_TIMESTAMP_RELATIVE_MAX] = "";
-
-                        format_timestamp(tstamp, sizeof(tstamp), t->next_elapse);
-                        nextlen = MAX(nextlen, strlen(tstamp) + 1);
-
-                        format_timestamp_relative(trel, sizeof(trel), t->next_elapse);
-                        leftlen = MAX(leftlen, strlen(trel));
-                }
-
-                if (t->last_trigger > 0) {
-                        char tstamp[FORMAT_TIMESTAMP_MAX] = "", trel[FORMAT_TIMESTAMP_RELATIVE_MAX] = "";
-
-                        format_timestamp(tstamp, sizeof(tstamp), t->last_trigger);
-                        lastlen = MAX(lastlen, strlen(tstamp) + 1);
-
-                        format_timestamp_relative(trel, sizeof(trel), t->last_trigger);
-                        passedlen = MAX(passedlen, strlen(trel));
-                }
-
-                unitlen = MAX(unitlen, strlen(t->id) + (t->machine ? strlen(t->machine)+1 : 0));
-
-                STRV_FOREACH(a, t->triggered)
-                        ul += strlen(*a) + 2*(a != t->triggered);
+        table = table_new("next", "left", "last", "passed", "unit", "activates");
+        if (!table)
+                return log_oom();
 
-                activatelen = MAX(activatelen, ul);
-        }
+        table_set_header(table, !arg_no_legend);
+        if (arg_full)
+                table_set_width(table, 0);
 
         if (n > 0) {
-                if (!arg_no_legend)
-                        printf("%-*s %-*s %-*s %-*s %-*s %s\n",
-                               nextlen,   "NEXT",
-                               leftlen,   "LEFT",
-                               lastlen,   "LAST",
-                               passedlen, "PASSED",
-                               unitlen,   "UNIT",
-                                          "ACTIVATES");
-
                 for (t = timer_infos; t < timer_infos + n; t++) {
-                        _cleanup_free_ char *j = NULL;
+                        _cleanup_free_ char *j = NULL, *activates = NULL;
                         const char *unit;
-                        char tstamp1[FORMAT_TIMESTAMP_MAX] = "n/a", trel1[FORMAT_TIMESTAMP_RELATIVE_MAX] = "n/a";
-                        char tstamp2[FORMAT_TIMESTAMP_MAX] = "n/a", trel2[FORMAT_TIMESTAMP_RELATIVE_MAX] = "n/a";
-                        char **a;
-
-                        format_timestamp(tstamp1, sizeof(tstamp1), t->next_elapse);
-                        format_timestamp_relative(trel1, sizeof(trel1), t->next_elapse);
-
-                        format_timestamp(tstamp2, sizeof(tstamp2), t->last_trigger);
-                        format_timestamp_relative(trel2, sizeof(trel2), t->last_trigger);
 
                         if (t->machine) {
                                 j = strjoin(t->machine, ":", t->id);
@@ -1379,26 +1259,34 @@ static int output_timers_list(struct timer_info *timer_infos, unsigned n) {
                         } else
                                 unit = t->id;
 
-                        printf("%-*s %-*s %-*s %-*s %-*s",
-                               nextlen, tstamp1, leftlen, trel1, lastlen, tstamp2, passedlen, trel2, unitlen, unit);
+                        activates = strv_join(t->triggered, ", ");
+                        if (!activates)
+                                return log_oom();
 
-                        STRV_FOREACH(a, t->triggered)
-                                printf("%s %s",
-                                       a == t->triggered ? "" : ",", *a);
-                        printf("\n");
+                        r = table_add_many(table,
+                                           TABLE_TIMESTAMP, t->next_elapse,
+                                           TABLE_TIMESTAMP_RELATIVE, t->next_elapse,
+                                           TABLE_TIMESTAMP, t->last_trigger,
+                                           TABLE_TIMESTAMP_RELATIVE, t->last_trigger,
+                                           TABLE_STRING, unit,
+                                           TABLE_STRING, activates);
+                        if (r < 0)
+                                return table_log_add_error(r);
                 }
 
                 on = ansi_highlight();
                 off = ansi_normal();
-                if (!arg_no_legend)
-                        printf("\n");
         } else {
                 on = ansi_highlight_red();
                 off = ansi_normal();
         }
 
+        r = table_print(table, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to print the table: %m");
+
         if (!arg_no_legend) {
-                printf("%s%u timers listed.%s\n", on, n, off);
+                printf("\n%s%u timers listed.%s\n", on, n, off);
                 if (!arg_all)
                         printf("Pass --all to see loaded but inactive timers, too.\n");
         }
@@ -1549,42 +1437,22 @@ static bool output_show_unit_file(const UnitFileList *u, char **states, char **p
         return true;
 }
 
-static void output_unit_file_list(const UnitFileList *units, unsigned c) {
-        unsigned max_id_len, id_cols, state_cols, preset_cols;
+static int output_unit_file_list(const UnitFileList *units, unsigned c) {
+        _cleanup_(table_unrefp) Table *table = NULL;
         const UnitFileList *u;
+        int r;
 
-        max_id_len = STRLEN("UNIT FILE");
-        state_cols = STRLEN("STATE");
-        preset_cols = STRLEN("VENDOR PRESET");
-
-        for (u = units; u < units + c; u++) {
-                max_id_len = MAX(max_id_len, strlen(basename(u->path)));
-                state_cols = MAX(state_cols, strlen(unit_file_state_to_string(u->state)));
-        }
-
-        if (!arg_full) {
-                unsigned basic_cols;
-
-                id_cols = MIN(max_id_len, 25u);
-                basic_cols = 1 + id_cols + state_cols;
-                if (basic_cols < (unsigned) columns())
-                        id_cols += MIN(columns() - basic_cols, max_id_len - id_cols);
-        } else
-                id_cols = max_id_len;
+        table = table_new("unit file", "state", "vendor preset");
+        if (!table)
+                return log_oom();
 
-        if (!arg_no_legend && c > 0)
-                printf("%s%-*s %-*s %-*s%s\n",
-                       ansi_underline(),
-                       id_cols, "UNIT FILE",
-                       state_cols, "STATE",
-                       preset_cols, "VENDOR PRESET",
-                       ansi_normal());
+        table_set_header(table, !arg_no_legend);
+        if (arg_full)
+                table_set_width(table, 0);
 
         for (u = units; u < units + c; u++) {
                 const char *on_underline = NULL, *on_unit_color = NULL, *id;
-                const char *on_preset_color = NULL, *off_preset = NULL, *unit_preset_str;
-                _cleanup_free_ char *e = NULL;
-                int r;
+                const char *on_preset_color = NULL, *unit_preset_str;
                 bool underline;
 
                 underline = u + 1 < units + c &&
@@ -1601,6 +1469,8 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
                         on_unit_color = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
                 else if (u->state == UNIT_FILE_ENABLED)
                         on_unit_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green();
+                else
+                        on_unit_color = on_underline;
 
                 id = basename(u->path);
 
@@ -1616,20 +1486,25 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
                         on_preset_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green();
                 }
 
-                if (on_underline || on_preset_color)
-                        off_preset = ansi_normal();
-
-                e = arg_full ? NULL : ellipsize(id, id_cols, 33);
-
-                printf("%s%-*s %s%-*s %s%-*s%s\n",
-                       strempty(on_underline),
-                       id_cols, e ? e : id,
-                       strempty(on_unit_color), state_cols, unit_file_state_to_string(u->state),
-                       strempty(on_preset_color), preset_cols, unit_preset_str, strempty(off_preset));
+                r = table_add_many(table,
+                                   TABLE_STRING, id,
+                                   TABLE_SET_COLOR, 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));
+                if (r < 0)
+                        return table_log_add_error(r);
         }
 
+        r = table_print(table, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to print the table: %m");
+
         if (!arg_no_legend)
                 printf("\n%u unit files listed.\n", c);
+
+        return 0;
 }
 
 static int list_unit_files(int argc, char *argv[], void *userdata) {
@@ -1772,7 +1647,9 @@ static int list_unit_files(int argc, char *argv[], void *userdata) {
         (void) pager_open(arg_pager_flags);
 
         typesafe_qsort(units, c, compare_unit_file_list);
-        output_unit_file_list(units, c);
+        r = output_unit_file_list(units, c);
+        if (r < 0)
+                return r;
 
         if (install_client_side())
                 for (unit = units; unit < units + c; unit++)
@@ -1911,30 +1788,39 @@ static int list_dependencies_one(
 }
 
 static int list_dependencies(int argc, char *argv[], void *userdata) {
-        _cleanup_strv_free_ char **units = NULL;
-        _cleanup_free_ char *unit = NULL;
-        const char *u;
+        _cleanup_strv_free_ char **units = NULL, **done = NULL;
+        char **u, **patterns;
         sd_bus *bus;
         int r;
 
-        if (argv[1]) {
-                r = unit_name_mangle(argv[1], arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, &unit);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to mangle unit name: %m");
-
-                u = unit;
-        } else
-                u = SPECIAL_DEFAULT_TARGET;
-
         r = acquire_bus(BUS_MANAGER, &bus);
         if (r < 0)
                 return r;
 
+        patterns = strv_skip(argv, 1);
+        if (strv_isempty(patterns)) {
+                units = strv_new(SPECIAL_DEFAULT_TARGET);
+                if (!units)
+                        return log_oom();
+        } else {
+                r = expand_names(bus, patterns, NULL, &units, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to expand names: %m");
+        }
+
         (void) pager_open(arg_pager_flags);
 
-        puts(u);
+        STRV_FOREACH(u, units) {
+                if (u != units)
+                        puts("");
+
+                puts(*u);
+                r = list_dependencies_one(bus, *u, 0, &done, 0);
+                if (r < 0)
+                        return r;
+        }
 
-        return list_dependencies_one(bus, u, 0, &units, 0);
+        return 0;
 }
 
 struct machine_info {
@@ -2081,94 +1967,77 @@ static int get_machine_list(
         return c;
 }
 
-static void output_machines_list(struct machine_info *machine_infos, unsigned n) {
+static int output_machines_list(struct machine_info *machine_infos, unsigned n) {
+        _cleanup_(table_unrefp) Table *table = NULL;
         struct machine_info *m;
-        unsigned
-                circle_len = 0,
-                namelen = STRLEN("NAME"),
-                statelen = STRLEN("STATE"),
-                failedlen = STRLEN("FAILED"),
-                jobslen = STRLEN("JOBS");
         bool state_missing = false;
+        int r;
 
         assert(machine_infos || n == 0);
 
-        for (m = machine_infos; m < machine_infos + n; m++) {
-                namelen = MAX(namelen,
-                              strlen(m->name) + (m->is_host ? STRLEN(" (host)") : 0));
-                statelen = MAX(statelen, strlen_ptr(m->state));
-                failedlen = MAX(failedlen, DECIMAL_STR_WIDTH(m->n_failed_units));
-                jobslen = MAX(jobslen, DECIMAL_STR_WIDTH(m->n_jobs));
-
-                if (!arg_plain && m->state && !streq(m->state, "running"))
-                        circle_len = 2;
-        }
-
-        if (!arg_no_legend) {
-                if (circle_len > 0)
-                        fputs("  ", stdout);
+        table = table_new("", "name", "state", "failed", "jobs");
+        if (!table)
+                return log_oom();
 
-                printf("%-*s %-*s %-*s %-*s\n",
-                         namelen, "NAME",
-                        statelen, "STATE",
-                       failedlen, "FAILED",
-                         jobslen, "JOBS");
-        }
+        table_set_header(table, !arg_no_legend);
+        if (arg_full)
+                table_set_width(table, 0);
 
         for (m = machine_infos; m < machine_infos + n; m++) {
-                const char *on_state = "", *off_state = "";
-                const char *on_failed = "", *off_failed = "";
+                _cleanup_free_ char *mname = NULL;
+                const char *on_state = "", *on_failed = "";
                 bool circle = false;
 
                 if (streq_ptr(m->state, "degraded")) {
                         on_state = ansi_highlight_red();
-                        off_state = ansi_normal();
                         circle = true;
                 } else if (!streq_ptr(m->state, "running")) {
                         on_state = ansi_highlight_yellow();
-                        off_state = ansi_normal();
                         circle = true;
                 }
 
-                if (m->n_failed_units > 0) {
+                if (m->n_failed_units > 0)
                         on_failed = ansi_highlight_red();
-                        off_failed = ansi_normal();
-                } else
-                        on_failed = off_failed = "";
-
-                if (circle_len > 0)
-                        printf("%s%s%s ", on_state, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ", off_state);
+                else
+                        on_failed =  "";
 
                 if (!m->state)
                         state_missing = true;
 
                 if (m->is_host)
-                        printf("%-*s (host) %s%-*s%s %s%*" PRIu32 "%s %*" PRIu32 "\n",
-                               (int) (namelen - strlen(" (host)")),
-                               strna(m->name),
-                               on_state, statelen, strna(m->state), off_state,
-                               on_failed, failedlen, m->n_failed_units, off_failed,
-                               jobslen, m->n_jobs);
-                else
-                        printf("%-*s %s%-*s%s %s%*" PRIu32 "%s %*" PRIu32 "\n",
-                               namelen, strna(m->name),
-                               on_state, statelen, strna(m->state), off_state,
-                               on_failed, failedlen, m->n_failed_units, off_failed,
-                               jobslen, m->n_jobs);
+                        mname = strjoin(strna(m->name), " (host)");
+
+                r = table_add_many(table,
+                                   TABLE_STRING, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ",
+                                   TABLE_SET_COLOR, on_state,
+                                   TABLE_STRING, m->is_host ? mname : strna(m->name),
+                                   TABLE_STRING, strna(m->state),
+                                   TABLE_SET_COLOR, on_state,
+                                   TABLE_UINT32, m->n_failed_units,
+                                   TABLE_SET_COLOR, on_failed,
+                                   TABLE_UINT32, m->n_jobs);
+                if (r < 0)
+                        return table_log_add_error(r);
         }
 
+        r = table_print(table, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to print the table: %m");
+
         if (!arg_no_legend) {
                 printf("\n");
                 if (state_missing && geteuid() != 0)
                         printf("Notice: some information only available to privileged users was not shown.\n");
                 printf("%u machines listed.\n", n);
         }
+
+        return 0;
 }
 
 static int list_machines(int argc, char *argv[], void *userdata) {
         struct machine_info *machine_infos = NULL;
         sd_bus *bus;
-        int r;
+        int r, rc;
 
         r = acquire_bus(BUS_MANAGER, &bus);
         if (r < 0)
@@ -2181,10 +2050,10 @@ static int list_machines(int argc, char *argv[], void *userdata) {
         (void) pager_open(arg_pager_flags);
 
         typesafe_qsort(machine_infos, r, compare_machine_info);
-        output_machines_list(machine_infos, r);
+        rc = output_machines_list(machine_infos, r);
         free_machines_list(machine_infos, r);
 
-        return 0;
+        return rc;
 }
 
 static int get_default(int argc, char *argv[], void *userdata) {
@@ -2292,7 +2161,7 @@ finish:
         return r;
 }
 
-static int output_waiting_jobs(sd_bus *bus, uint32_t id, const char *method, const char *prefix) {
+static int output_waiting_jobs(sd_bus *bus, Table *table, uint32_t id, const char *method, const char *prefix) {
         _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 *name, *type;
@@ -2317,8 +2186,22 @@ static int output_waiting_jobs(sd_bus *bus, uint32_t id, const char *method, con
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        while ((r = sd_bus_message_read(reply, "(usssoo)", &other_id, &name, &type, NULL, NULL, NULL)) > 0)
-                printf("%s %u (%s/%s)\n", prefix, other_id, name, type);
+        while ((r = sd_bus_message_read(reply, "(usssoo)", &other_id, &name, &type, NULL, NULL, NULL)) > 0) {
+                _cleanup_free_ char *row = NULL;
+                int rc;
+
+                if (asprintf(&row, "%s %u (%s/%s)", prefix, other_id, name, type) < 0)
+                        return log_oom();
+
+                rc = table_add_many(table,
+                                    TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_RIGHT),
+                                    TABLE_STRING, row,
+                                    TABLE_EMPTY,
+                                    TABLE_EMPTY);
+                if (rc < 0)
+                        return table_log_add_error(r);
+        }
+
         if (r < 0)
                 return bus_log_parse_error(r);
 
@@ -2334,11 +2217,11 @@ struct job_info {
         const char *name, *type, *state;
 };
 
-static void output_jobs_list(sd_bus *bus, const struct job_info* jobs, unsigned n, bool skipped) {
-        unsigned id_len, unit_len, type_len, state_len;
+static int output_jobs_list(sd_bus *bus, const struct job_info* jobs, unsigned n, bool skipped) {
+        _cleanup_(table_unrefp) Table *table = NULL;
         const struct job_info *j;
         const char *on, *off;
-        bool shorten = false;
+        int r;
 
         assert(n == 0 || jobs);
 
@@ -2349,66 +2232,54 @@ static void output_jobs_list(sd_bus *bus, const struct job_info* jobs, unsigned
 
                         printf("%sNo jobs %s.%s\n", on, skipped ? "listed" : "running", off);
                 }
-                return;
+                return 0;
         }
 
         (void) pager_open(arg_pager_flags);
 
-        id_len = STRLEN("JOB");
-        unit_len = STRLEN("UNIT");
-        type_len = STRLEN("TYPE");
-        state_len = STRLEN("STATE");
-
-        for (j = jobs; j < jobs + n; j++) {
-                uint32_t id = j->id;
-                assert(j->name && j->type && j->state);
-
-                id_len = MAX(id_len, DECIMAL_STR_WIDTH(id));
-                unit_len = MAX(unit_len, strlen(j->name));
-                type_len = MAX(type_len, strlen(j->type));
-                state_len = MAX(state_len, strlen(j->state));
-        }
-
-        if (!arg_full && id_len + 1 + unit_len + type_len + 1 + state_len > columns()) {
-                unit_len = MAX(33u, columns() - id_len - type_len - state_len - 3);
-                shorten = true;
-        }
+        table = table_new("job", "unit", "type", "state");
+        if (!table)
+                return log_oom();
 
-        if (!arg_no_legend)
-                printf("%*s %-*s %-*s %-*s\n",
-                       id_len, "JOB",
-                       unit_len, "UNIT",
-                       type_len, "TYPE",
-                       state_len, "STATE");
+        table_set_header(table, !arg_no_legend);
+        if (arg_full)
+                table_set_width(table, 0);
 
         for (j = jobs; j < jobs + n; j++) {
-                _cleanup_free_ char *e = NULL;
-
-                if (streq(j->state, "running")) {
+                if (streq(j->state, "running"))
                         on = ansi_highlight();
-                        off = ansi_normal();
-                } else
-                        on = off = "";
+                else
+                        on =  "";
+
 
-                e = shorten ? ellipsize(j->name, unit_len, 33) : NULL;
-                printf("%*u %s%-*s%s %-*s %s%-*s%s\n",
-                       id_len, j->id,
-                       on, unit_len, e ? e : j->name, off,
-                       type_len, j->type,
-                       on, state_len, j->state, off);
+                r = table_add_many(table,
+                                   TABLE_UINT, j->id,
+                                   TABLE_STRING, j->name,
+                                   TABLE_SET_COLOR, on,
+                                   TABLE_STRING, j->type,
+                                   TABLE_STRING, j->state,
+                                   TABLE_SET_COLOR, on);
+                if (r < 0)
+                        return table_log_add_error(r);
 
                 if (arg_jobs_after)
-                        output_waiting_jobs(bus, j->id, "GetJobAfter", "\twaiting for job");
+                        output_waiting_jobs(bus, table, j->id, "GetJobAfter", "\twaiting for job");
                 if (arg_jobs_before)
-                        output_waiting_jobs(bus, j->id, "GetJobBefore", "\tblocking job");
+                        output_waiting_jobs(bus, table, j->id, "GetJobBefore", "\tblocking job");
         }
 
+        r = table_print(table, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to print the table: %m");
+
         if (!arg_no_legend) {
                 on = ansi_highlight();
                 off = ansi_normal();
 
                 printf("\n%s%u jobs listed%s.\n", on, n, off);
         }
+
+        return 0;
 }
 
 static bool output_show_job(struct job_info *job, char **patterns) {
@@ -2469,8 +2340,7 @@ static int list_jobs(int argc, char *argv[], void *userdata) {
 
         (void) pager_open(arg_pager_flags);
 
-        output_jobs_list(bus, jobs, c, skipped);
-        return 0;
+        return output_jobs_list(bus, jobs, c, skipped);
 }
 
 static int cancel_job(int argc, char *argv[], void *userdata) {
@@ -4148,6 +4018,8 @@ typedef struct UnitStatusInfo {
 
         int exit_code, exit_status;
 
+        const char *log_namespace;
+
         usec_t condition_timestamp;
         bool condition_result;
         LIST_HEAD(UnitCondition, conditions);
@@ -4617,11 +4489,11 @@ static void print_status_info(
 
                         printf(" (");
                         if (i->memory_min > 0) {
-                                printf("%smin: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_min));
+                                printf("%smin: %s", prefix, format_bytes_cgroup_protection(buf, sizeof(buf), i->memory_min));
                                 prefix = " ";
                         }
                         if (i->memory_low > 0) {
-                                printf("%slow: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_low));
+                                printf("%slow: %s", prefix, format_bytes_cgroup_protection(buf, sizeof(buf), i->memory_low));
                                 prefix = " ";
                         }
                         if (i->memory_high != CGROUP_LIMIT_MAX) {
@@ -4686,6 +4558,7 @@ static void print_status_info(
                 show_journal_by_unit(
                                 stdout,
                                 i->id,
+                                i->log_namespace,
                                 arg_output,
                                 0,
                                 i->inactive_exit_timestamp_monotonic,
@@ -5632,6 +5505,7 @@ static int show_one(
                 { "ExecMainExitTimestamp",          "t",               NULL,           offsetof(UnitStatusInfo, exit_timestamp)                    },
                 { "ExecMainCode",                   "i",               NULL,           offsetof(UnitStatusInfo, exit_code)                         },
                 { "ExecMainStatus",                 "i",               NULL,           offsetof(UnitStatusInfo, exit_status)                       },
+                { "LogNamespace",                   "s",               NULL,           offsetof(UnitStatusInfo, log_namespace)                     },
                 { "ConditionTimestamp",             "t",               NULL,           offsetof(UnitStatusInfo, condition_timestamp)               },
                 { "ConditionResult",                "b",               NULL,           offsetof(UnitStatusInfo, condition_result)                  },
                 { "Conditions",                     "a(sbbsi)",        map_conditions, 0                                                           },
@@ -6727,7 +6601,7 @@ static int enable_sysv_units(const char *verb, char **args) {
 
                 j = unit_file_exists(arg_scope, &paths, name);
                 if (j < 0 && !IN_SET(j, -ELOOP, -ERFKILL, -EADDRNOTAVAIL))
-                        return log_error_errno(j, "Failed to lookup unit file state: %m");
+                        return log_error_errno(j, "Failed to look up unit file state: %m");
                 found_native = j != 0;
 
                 /* If we have both a native unit and a SysV script, enable/disable them both (below); for is-enabled,
@@ -7952,9 +7826,9 @@ static int systemctl_help(void) {
                "  help PATTERN...|PID...              Show manual for one or more units\n"
                "  reset-failed [PATTERN...]           Reset failed state for all, one, or more\n"
                "                                      units\n"
-               "  list-dependencies [UNIT]            Recursively show units which are required\n"
-               "                                      or wanted by this unit or by which this\n"
-               "                                      unit is required or wanted"
+               "  list-dependencies [UNIT...]         Recursively show units which are required\n"
+               "                                      or wanted by the units or by which those\n"
+               "                                      units are required or wanted"
                "\n%3$sUnit File Commands:%4$s\n"
                "  list-unit-files [PATTERN...]        List installed unit files\n"
                "  enable [UNIT...|PATH...]            Enable one or more unit files\n"
@@ -9253,7 +9127,7 @@ static int systemctl_main(int argc, char *argv[]) {
                 { "link",                  2,        VERB_ANY, 0,                enable_unit             },
                 { "revert",                2,        VERB_ANY, 0,                enable_unit             },
                 { "switch-root",           2,        VERB_ANY, VERB_ONLINE_ONLY, switch_root             },
-                { "list-dependencies",     VERB_ANY, 2,        VERB_ONLINE_ONLY, list_dependencies       },
+                { "list-dependencies",     VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, list_dependencies       },
                 { "set-default",           2,        2,        0,                set_default             },
                 { "get-default",           VERB_ANY, 1,        0,                get_default             },
                 { "set-property",          3,        VERB_ANY, VERB_ONLINE_ONLY, set_property            },
index b3ee7bbc24e371a91fd2f530279c83678bdbc778..8158ee733e22e57f9e7c7e6508fbeda129346fa8 100644 (file)
@@ -45,6 +45,18 @@ typedef void (*_sd_destroy_t)(void *userdata);
 #  define _sd_pure_ __attribute__((__pure__))
 #endif
 
+/* Note that strictly speaking __deprecated__ has been available before GCC 6. However, starting with GCC 6
+ * it also works on enum values, which we are interested in. Since this is a developer-facing feature anyway
+ * (as opposed to build engineer-facing), let's hence conditionalize this to gcc 6, given that the developers
+ * are probably going to use something newer anyway. */
+#ifndef _sd_deprecated_
+#  if __GNUC__ >= 6
+#    define _sd_deprecated_ __attribute__((__deprecated__))
+#  else
+#    define _sd_deprecated_
+#  endif
+#endif
+
 #ifndef _SD_STRINGIFY
 #  define _SD_XSTRINGIFY(x) #x
 #  define _SD_STRINGIFY(x) _SD_XSTRINGIFY(x)
index 3c9792e4975e235fc750690f61997316c6907dce..e6f3298745722e28e6399f4a9f26fdec829d213b 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <inttypes.h>
 #include <stdarg.h>
+#include <stdio.h>
 #include <sys/types.h>
 #include <sys/uio.h>
 
@@ -105,6 +106,11 @@ enum {
         SD_BUS_NAME_QUEUE             = 1ULL << 2
 };
 
+enum {
+        SD_BUS_MESSAGE_DUMP_WITH_HEADER  = 1ULL << 0,
+        SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY = 1ULL << 1,
+};
+
 /* Callbacks */
 
 typedef int (*sd_bus_message_handler_t)(sd_bus_message *m, void *userdata, sd_bus_error *ret_error);
@@ -201,6 +207,7 @@ int sd_bus_process(sd_bus *bus, sd_bus_message **r);
 int sd_bus_process_priority(sd_bus *bus, int64_t max_priority, sd_bus_message **r);
 int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec);
 int sd_bus_flush(sd_bus *bus);
+int sd_bus_enqueue_for_read(sd_bus *bus, sd_bus_message *m);
 
 sd_bus_slot* sd_bus_get_current_slot(sd_bus *bus);
 sd_bus_message* sd_bus_get_current_message(sd_bus *bus);
@@ -330,6 +337,8 @@ int sd_bus_message_at_end(sd_bus_message *m, int complete);
 int sd_bus_message_rewind(sd_bus_message *m, int complete);
 int sd_bus_message_sensitive(sd_bus_message *m);
 
+int sd_bus_message_dump(sd_bus_message *m, FILE *f, uint64_t flags);
+
 /* Bus management */
 
 int sd_bus_get_unique_name(sd_bus *bus, const char **unique);
index b4bf3b91762f8d316db6d2dbd0396077e43e3c04..29ff40ab2618f5f965039832e76fad1f234ca1bd 100644 (file)
@@ -64,13 +64,15 @@ typedef struct sd_journal sd_journal;
 
 /* Open flags */
 enum {
-        SD_JOURNAL_LOCAL_ONLY   = 1 << 0,
-        SD_JOURNAL_RUNTIME_ONLY = 1 << 1,
-        SD_JOURNAL_SYSTEM       = 1 << 2,
-        SD_JOURNAL_CURRENT_USER = 1 << 3,
-        SD_JOURNAL_OS_ROOT      = 1 << 4,
-
-        SD_JOURNAL_SYSTEM_ONLY = SD_JOURNAL_SYSTEM /* deprecated name */
+        SD_JOURNAL_LOCAL_ONLY                = 1 << 0,
+        SD_JOURNAL_RUNTIME_ONLY              = 1 << 1,
+        SD_JOURNAL_SYSTEM                    = 1 << 2,
+        SD_JOURNAL_CURRENT_USER              = 1 << 3,
+        SD_JOURNAL_OS_ROOT                   = 1 << 4,
+        SD_JOURNAL_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 */
 };
 
 /* Wakeup event types */
@@ -81,11 +83,12 @@ enum {
 };
 
 int sd_journal_open(sd_journal **ret, int flags);
+int sd_journal_open_namespace(sd_journal **ret, const char *name_space, int flags);
 int sd_journal_open_directory(sd_journal **ret, const char *path, int flags);
 int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags);
 int sd_journal_open_files(sd_journal **ret, const char **paths, int flags);
 int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags);
-int sd_journal_open_container(sd_journal **ret, const char *machine, int flags); /* deprecated */
+int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) _sd_deprecated_; /* deprecated */
 void sd_journal_close(sd_journal *j);
 
 int sd_journal_previous(sd_journal *j);
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 f0852319347be3620cb213c65055b06b3f9d7885..011e40d8a5c68552a637503a62f63924408acc54 100644 (file)
@@ -74,6 +74,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 08a2df707bedf8b813da846385f60c6b294804b8..f7cc7e09009e580bf46ba650a41a5e52f8122830 100644 (file)
@@ -39,6 +39,7 @@ typedef struct Item {
         ItemType type;
 
         char *name;
+        char *group_name;
         char *uid_path;
         char *gid_path;
         char *description;
@@ -93,6 +94,12 @@ STATIC_DESTRUCTOR_REGISTER(database_groups, set_free_freep);
 STATIC_DESTRUCTOR_REGISTER(uid_range, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
 
+static int errno_is_not_exists(int code) {
+        /* See getpwnam(3) and getgrnam(3): those codes and others can be returned if the user or group are
+         * not found. */
+        return IN_SET(code, 0, ENOENT, ESRCH, EBADF, EPERM);
+}
+
 static int load_user_database(void) {
         _cleanup_fclose_ FILE *f = NULL;
         const char *passwd_path;
@@ -192,7 +199,7 @@ static int load_group_database(void) {
 static int make_backup(const char *target, const char *x) {
         _cleanup_close_ int src = -1;
         _cleanup_fclose_ FILE *dst = NULL;
-        _cleanup_free_ char *temp = NULL;
+        _cleanup_free_ char *dst_tmp = NULL;
         char *backup;
         struct timespec ts[2];
         struct stat st;
@@ -209,7 +216,7 @@ static int make_backup(const char *target, const char *x) {
         if (fstat(src, &st) < 0)
                 return -errno;
 
-        r = fopen_temporary_label(target, x, &dst, &temp);
+        r = fopen_temporary_label(target, x, &dst, &dst_tmp);
         if (r < 0)
                 return r;
 
@@ -223,7 +230,7 @@ static int make_backup(const char *target, const char *x) {
         backup = strjoina(x, "-");
 
         /* Copy over the access mask */
-        r = fchmod_and_chown(fileno(dst), st.st_mode & 07777, st.st_uid, st.st_gid);
+        r = chmod_and_chown_unsafe(dst_tmp, st.st_mode & 07777, st.st_uid, st.st_gid);
         if (r < 0)
                 log_warning_errno(r, "Failed to change access mode or ownership of %s: %m", backup);
 
@@ -236,7 +243,7 @@ static int make_backup(const char *target, const char *x) {
         if (r < 0)
                 goto fail;
 
-        if (rename(temp, backup) < 0) {
+        if (rename(dst_tmp, backup) < 0) {
                 r = -errno;
                 goto fail;
         }
@@ -244,7 +251,7 @@ static int make_backup(const char *target, const char *x) {
         return 0;
 
 fail:
-        (void) unlink(temp);
+        (void) unlink(dst_tmp);
         return r;
 }
 
@@ -338,13 +345,13 @@ static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
 }
 #endif
 
-static int sync_rights(FILE *from, FILE *to) {
+static int sync_rights(FILE *from, const char *to) {
         struct stat st;
 
         if (fstat(fileno(from), &st) < 0)
                 return -errno;
 
-        return fchmod_and_chown(fileno(to), st.st_mode & 07777, st.st_uid, st.st_gid);
+        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) {
@@ -382,7 +389,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);
+                r = sync_rights(original, passwd_tmp);
                 if (r < 0)
                         return r;
 
@@ -484,7 +491,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);
+                r = sync_rights(original, shadow_tmp);
                 if (r < 0)
                         return r;
 
@@ -581,7 +588,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);
+                r = sync_rights(original, group_tmp);
                 if (r < 0)
                         return r;
 
@@ -680,7 +687,7 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, ch
         if (original) {
                 struct sgrp *sg;
 
-                r = sync_rights(original, gshadow);
+                r = sync_rights(original, gshadow_tmp);
                 if (r < 0)
                         return r;
 
@@ -970,7 +977,7 @@ static int add_user(Item *i) {
 
                         return 0;
                 }
-                if (!IN_SET(errno, 0, ENOENT))
+                if (!errno_is_not_exists(errno))
                         return log_error_errno(errno, "Failed to check if user %s already exists: %m", i->name);
         }
 
@@ -1085,18 +1092,15 @@ static int gid_is_ok(gid_t gid) {
         return 1;
 }
 
-static int add_group(Item *i) {
+static int get_gid_by_name(const char *name, gid_t *gid) {
         void *z;
-        int r;
 
-        assert(i);
+        assert(gid);
 
         /* Check the database directly */
-        z = hashmap_get(database_by_groupname, i->name);
+        z = hashmap_get(database_by_groupname, name);
         if (z) {
-                log_debug("Group %s already exists.", i->name);
-                i->gid = PTR_TO_GID(z);
-                i->gid_set = true;
+                *gid = PTR_TO_GID(z);
                 return 0;
         }
 
@@ -1105,15 +1109,30 @@ static int add_group(Item *i) {
                 struct group *g;
 
                 errno = 0;
-                g = getgrnam(i->name);
+                g = getgrnam(name);
                 if (g) {
-                        log_debug("Group %s already exists.", i->name);
-                        i->gid = g->gr_gid;
-                        i->gid_set = true;
+                        *gid = g->gr_gid;
                         return 0;
                 }
-                if (!IN_SET(errno, 0, ENOENT))
-                        return log_error_errno(errno, "Failed to check if group %s already exists: %m", i->name);
+                if (!errno_is_not_exists(errno))
+                        return log_error_errno(errno, "Failed to check if group %s already exists: %m", name);
+        }
+
+        return -ENOENT;
+}
+
+static int add_group(Item *i) {
+        int r;
+
+        assert(i);
+
+        r = get_gid_by_name(i->name, &i->gid);
+        if (r != -ENOENT) {
+                if (r < 0)
+                        return r;
+                log_debug("Group %s already exists.", i->name);
+                i->gid_set = true;
+                return 0;
         }
 
         /* Try to use the suggested numeric gid */
@@ -1214,14 +1233,22 @@ static int process_item(Item *i) {
         case ADD_USER: {
                 Item *j;
 
-                j = ordered_hashmap_get(groups, i->name);
+                j = ordered_hashmap_get(groups, i->group_name ?: i->name);
                 if (j && j->todo_group) {
-                        /* When the group with the same name is already in queue,
+                        /* When a group with the target name is already in queue,
                          * use the information about the group and do not create
                          * duplicated group entry. */
                         i->gid_set = j->gid_set;
                         i->gid = j->gid;
                         i->id_set_strict = true;
+                } else if (i->group_name) {
+                        /* When a group name was given instead of a GID and it's
+                         * not in queue, then it must already exist. */
+                        r = get_gid_by_name(i->group_name, &i->gid);
+                        if (r < 0)
+                                return log_error_errno(r, "Group %s not found.", i->group_name);
+                        i->gid_set = true;
+                        i->id_set_strict = true;
                 } else {
                         r = add_group(i);
                         if (r < 0)
@@ -1244,6 +1271,7 @@ static Item* item_free(Item *i) {
                 return NULL;
 
         free(i->name);
+        free(i->group_name);
         free(i->uid_path);
         free(i->gid_path);
         free(i->description);
@@ -1560,10 +1588,15 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                                 _cleanup_free_ char *uid = NULL, *gid = NULL;
                                 if (split_pair(resolved_id, ":", &uid, &gid) == 0) {
                                         r = parse_gid(gid, &i->gid);
-                                        if (r < 0)
-                                                return log_error_errno(r, "Failed to parse GID: '%s': %m", id);
-                                        i->gid_set = true;
-                                        i->id_set_strict = true;
+                                        if (r < 0) {
+                                                if (valid_user_group_name(gid))
+                                                        i->group_name = TAKE_PTR(gid);
+                                                else
+                                                        return log_error_errno(r, "Failed to parse GID: '%s': %m", id);
+                                        } else {
+                                                i->gid_set = true;
+                                                i->id_set_strict = true;
+                                        }
                                         free_and_replace(resolved_id, uid);
                                 }
                                 if (!streq(resolved_id, "-")) {
index 4d358b8e345f63c80a5cc9c75d5d327bd9a50c93..fdb9e3ecb789600f66ed77c10ddb08058e53010b 100755 (executable)
@@ -6,6 +6,9 @@ for header in sys.argv[2:]:
     print('#include "{}"'.format(header.split('/')[-1]))
 
 print('''
+/* We want to check deprecated symbols too, without complaining */
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
 const void* symbols[] = {''')
 
 for line in open(sys.argv[1]):
index eb66161861e75a1f5907c44327db253f7a5c47e4..7c55f65be1cdeabe086175fc5ce7db1af09ab137 100644 (file)
@@ -325,6 +325,10 @@ tests += [
          [],
          []],
 
+        [['src/test/test-sysctl-util.c'],
+         [],
+         []],
+
         [['src/test/test-user-util.c'],
          [],
          []],
index fa91869cf5bb011781b309ffb27279756fd08cd0..098bb35bce67ebebc8fc43e99d743d21eae4d1f2 100644 (file)
@@ -1,24 +1,26 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
-#include "alloc-util.h"
 #include "ask-password-api.h"
-#include "log.h"
 #include "strv.h"
+#include "tests.h"
 
-static void ask_password(void) {
+static void test_ask_password(void) {
         int r;
         _cleanup_strv_free_ char **ret = NULL;
 
-        r = ask_password_tty(-1, "hello?", "da key", 0, 0, NULL, &ret);
-        assert(r >= 0);
-        assert(strv_length(ret) == 1);
-
-        log_info("Got %s", *ret);
+        r = ask_password_tty(-1, "hello?", "da key", 0, ASK_PASSWORD_CONSOLE_COLOR, NULL, &ret);
+        if (r == -ECANCELED)
+                assert_se(ret == NULL);
+        else {
+                assert_se(r >= 0);
+                assert_se(strv_length(ret) == 1);
+                log_info("Got \"%s\"", *ret);
+        }
 }
 
 int main(int argc, char **argv) {
-        log_parse_environment();
+        test_setup_logging(LOG_DEBUG);
 
-        ask_password();
+        test_ask_password();
         return EXIT_SUCCESS;
 }
index 5bd0e3458c45eee29d1b037837807f8300c7aab7..9dbe4dccd136f442e521168d1de8ee39c747796d 100644 (file)
@@ -155,7 +155,7 @@ int main(int argc, char *argv[]) {
 
         r = btrfs_subvol_snapshot("/xxxquotatest", "/xxxquotatest2", BTRFS_SNAPSHOT_RECURSIVE|BTRFS_SNAPSHOT_QUOTA);
         if (r < 0)
-                log_error_errno(r, "Failed to setup snapshot: %m");
+                log_error_errno(r, "Failed to set up snapshot: %m");
 
         r = btrfs_qgroup_get_quota("/xxxquotatest2/beneath", 0, &quota);
         if (r < 0)
index 18b083d87fcb9d0abccd5d8996c580aa304ac122..510b1de72d104aea2e38af2c6d5117b3098f3208 100644 (file)
@@ -38,11 +38,11 @@ static void test_config_parse_iec_size_one(const char *rvalue, size_t expected)
         assert_se(expected == iec_size);
 }
 
-static void test_config_parse_si_size_one(const char *rvalue, size_t expected) {
-        size_t si_size = 0;
+static void test_config_parse_si_uint64_one(const char *rvalue, uint64_t expected) {
+        uint64_t si_uint64 = 0;
 
-        assert_se(config_parse_si_size("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &si_size, NULL) >= 0);
-        assert_se(expected == si_size);
+        assert_se(config_parse_si_uint64("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &si_uint64, NULL) >= 0);
+        assert_se(expected == si_uint64);
 }
 
 static void test_config_parse_int_one(const char *rvalue, int expected) {
@@ -125,17 +125,17 @@ static void test_config_parse_iec_size(void) {
         test_config_parse_iec_size_one("garbage", 0);
 }
 
-static void test_config_parse_si_size(void) {
-        test_config_parse_si_size_one("1024", 1024);
-        test_config_parse_si_size_one("2K", 2000);
-        test_config_parse_si_size_one("10M", 10 * 1000 * 1000);
-        test_config_parse_si_size_one("1G", 1 * 1000 * 1000 * 1000);
-        test_config_parse_si_size_one("0G", 0);
-        test_config_parse_si_size_one("0", 0);
-
-        test_config_parse_si_size_one("-982", 0);
-        test_config_parse_si_size_one("49874444198739873000000G", 0);
-        test_config_parse_si_size_one("garbage", 0);
+static void test_config_parse_si_uint64(void) {
+        test_config_parse_si_uint64_one("1024", 1024);
+        test_config_parse_si_uint64_one("2K", 2000);
+        test_config_parse_si_uint64_one("10M", 10 * 1000 * 1000);
+        test_config_parse_si_uint64_one("1G", 1 * 1000 * 1000 * 1000);
+        test_config_parse_si_uint64_one("0G", 0);
+        test_config_parse_si_uint64_one("0", 0);
+
+        test_config_parse_si_uint64_one("-982", 0);
+        test_config_parse_si_uint64_one("49874444198739873000000G", 0);
+        test_config_parse_si_uint64_one("garbage", 0);
 }
 
 static void test_config_parse_int(void) {
@@ -391,7 +391,7 @@ int main(int argc, char **argv) {
         test_config_parse_log_level();
         test_config_parse_log_facility();
         test_config_parse_iec_size();
-        test_config_parse_si_size();
+        test_config_parse_si_uint64();
         test_config_parse_int();
         test_config_parse_unsigned();
         test_config_parse_strv();
index 12685dad1373de390555d5b7a779f3221470d798..a1ccf605b11c6580dcea23903da1badf4d41a37e 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, &m);
+        r = dissect_image(d->fd, NULL, 0, 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 add17f9547dc6b803143d4cef265f1a8f54167a2..f6aae1eb1828d8432648cd043a1f036bc73127c6 100644 (file)
@@ -96,6 +96,22 @@ static void test_cunescape(void) {
 
         assert_se(cunescape("A=A\\\\x0aB", UNESCAPE_RELAX, &unescaped) >= 0);
         assert_se(streq_ptr(unescaped, "A=A\\x0aB"));
+        unescaped = mfree(unescaped);
+
+        assert_se(cunescape("\\x00\\x00\\x00", UNESCAPE_ACCEPT_NUL, &unescaped) == 3);
+        assert_se(memcmp(unescaped, "\0\0\0", 3) == 0);
+        unescaped = mfree(unescaped);
+
+        assert_se(cunescape("\\u0000\\u0000\\u0000", UNESCAPE_ACCEPT_NUL, &unescaped) == 3);
+        assert_se(memcmp(unescaped, "\0\0\0", 3) == 0);
+        unescaped = mfree(unescaped);
+
+        assert_se(cunescape("\\U00000000\\U00000000\\U00000000", UNESCAPE_ACCEPT_NUL, &unescaped) == 3);
+        assert_se(memcmp(unescaped, "\0\0\0", 3) == 0);
+        unescaped = mfree(unescaped);
+
+        assert_se(cunescape("\\000\\000\\000", UNESCAPE_ACCEPT_NUL, &unescaped) == 3);
+        assert_se(memcmp(unescaped, "\0\0\0", 3) == 0);
 }
 
 static void test_shell_escape_one(const char *s, const char *bad, const char *expected) {
index b3f8cc84345da32dbcb2967ed3902bdc4c3fd825..4e0fd7d5b4b7ce3da24518358419869304f54d3e 100644 (file)
@@ -294,6 +294,7 @@ static void test_exec_privatetmp(Manager *m) {
 
         test(__func__, m, "exec-privatetmp-yes.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
         test(__func__, m, "exec-privatetmp-no.service", 0, CLD_EXITED);
+        test(__func__, m, "exec-privatetmp-disabled-by-prefix.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
 
         unlink("/tmp/test-exec_privatetmp");
 }
@@ -560,6 +561,7 @@ static void test_exec_dynamicuser(Manager *m) {
 
         test(__func__, m, "exec-dynamicuser-statedir-migrate-step1.service", 0, CLD_EXITED);
         test(__func__, m, "exec-dynamicuser-statedir-migrate-step2.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
+        test(__func__, m, "exec-dynamicuser-statedir-migrate-step1.service", 0, CLD_EXITED);
 
         (void) rm_rf("/var/lib/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL);
         (void) rm_rf("/var/lib/test-dynamicuser-migrate2", REMOVE_ROOT|REMOVE_PHYSICAL);
@@ -756,6 +758,7 @@ static void test_exec_specifier(Manager *m) {
 static void test_exec_standardinput(Manager *m) {
         test(__func__, m, "exec-standardinput-data.service", 0, CLD_EXITED);
         test(__func__, m, "exec-standardinput-file.service", 0, CLD_EXITED);
+        test(__func__, m, "exec-standardinput-file-cat.service", 0, CLD_EXITED);
 }
 
 static void test_exec_standardoutput(Manager *m) {
@@ -786,6 +789,7 @@ static int run_tests(UnitFileScope scope, const test_entry tests[], char **patte
         assert_se(tests);
 
         r = manager_new(scope, MANAGER_TEST_RUN_BASIC, &m);
+        m->default_std_output = EXEC_OUTPUT_NULL; /* don't rely on host journald */
         if (manager_errno_skip_test(r))
                 return log_tests_skipped_errno(r, "manager_new");
         assert_se(r >= 0);
index 7bf62e48e20ea581d429a2416ac1dea39bbd8cf3..283cf157babb8a258ef5f1a7fde19a19d028745e 100644 (file)
@@ -5,6 +5,7 @@
 #include "alloc-util.h"
 #include "format-table.h"
 #include "string-util.h"
+#include "strv.h"
 #include "time-util.h"
 
 static void test_issue_9549(void) {
@@ -143,6 +144,118 @@ static void test_multiline(void) {
         formatted = mfree(formatted);
 }
 
+static void test_strv(void) {
+        _cleanup_(table_unrefp) Table *table = NULL;
+        _cleanup_free_ char *formatted = NULL;
+
+        assert_se(table = table_new("foo", "bar"));
+
+        assert_se(table_set_align_percent(table, TABLE_HEADER_CELL(1), 100) >= 0);
+
+        assert_se(table_add_many(table,
+                                 TABLE_STRV, STRV_MAKE("three", "different", "lines"),
+                                 TABLE_STRV, STRV_MAKE("two", "lines")) >= 0);
+
+        table_set_cell_height_max(table, 1);
+        assert_se(table_format(table, &formatted) >= 0);
+        fputs(formatted, stdout);
+        assert_se(streq(formatted,
+                        "FOO     BAR\n"
+                        "three… two…\n"));
+        formatted = mfree(formatted);
+
+        table_set_cell_height_max(table, 2);
+        assert_se(table_format(table, &formatted) >= 0);
+        fputs(formatted, stdout);
+        assert_se(streq(formatted,
+                        "FOO          BAR\n"
+                        "three        two\n"
+                        "different… lines\n"));
+        formatted = mfree(formatted);
+
+        table_set_cell_height_max(table, 3);
+        assert_se(table_format(table, &formatted) >= 0);
+        fputs(formatted, stdout);
+        assert_se(streq(formatted,
+                        "FOO         BAR\n"
+                        "three       two\n"
+                        "different lines\n"
+                        "lines          \n"));
+        formatted = mfree(formatted);
+
+        table_set_cell_height_max(table, (size_t) -1);
+        assert_se(table_format(table, &formatted) >= 0);
+        fputs(formatted, stdout);
+        assert_se(streq(formatted,
+                        "FOO         BAR\n"
+                        "three       two\n"
+                        "different lines\n"
+                        "lines          \n"));
+        formatted = mfree(formatted);
+
+        assert_se(table_add_many(table,
+                                 TABLE_STRING, "short",
+                                 TABLE_STRV, STRV_MAKE("a", "pair")) >= 0);
+
+        assert_se(table_add_many(table,
+                                 TABLE_STRV, STRV_MAKE("short2"),
+                                 TABLE_STRV, STRV_MAKE("a", "four", "line", "cell")) >= 0);
+
+        table_set_cell_height_max(table, 1);
+        assert_se(table_format(table, &formatted) >= 0);
+        fputs(formatted, stdout);
+        assert_se(streq(formatted,
+                        "FOO     BAR\n"
+                        "three… two…\n"
+                        "short    a…\n"
+                        "short2   a…\n"));
+        formatted = mfree(formatted);
+
+        table_set_cell_height_max(table, 2);
+        assert_se(table_format(table, &formatted) >= 0);
+        fputs(formatted, stdout);
+        assert_se(streq(formatted,
+                        "FOO          BAR\n"
+                        "three        two\n"
+                        "different… lines\n"
+                        "short          a\n"
+                        "            pair\n"
+                        "short2         a\n"
+                        "           four…\n"));
+        formatted = mfree(formatted);
+
+        table_set_cell_height_max(table, 3);
+        assert_se(table_format(table, &formatted) >= 0);
+        fputs(formatted, stdout);
+        assert_se(streq(formatted,
+                        "FOO         BAR\n"
+                        "three       two\n"
+                        "different lines\n"
+                        "lines          \n"
+                        "short         a\n"
+                        "           pair\n"
+                        "short2        a\n"
+                        "           four\n"
+                        "          line…\n"));
+        formatted = mfree(formatted);
+
+        table_set_cell_height_max(table, (size_t) -1);
+        assert_se(table_format(table, &formatted) >= 0);
+        fputs(formatted, stdout);
+        assert_se(streq(formatted,
+                        "FOO         BAR\n"
+                        "three       two\n"
+                        "different lines\n"
+                        "lines          \n"
+                        "short         a\n"
+                        "           pair\n"
+                        "short2        a\n"
+                        "           four\n"
+                        "           line\n"
+                        "           cell\n"));
+        formatted = mfree(formatted);
+}
+
 int main(int argc, char *argv[]) {
 
         _cleanup_(table_unrefp) Table *t = NULL;
@@ -285,6 +398,7 @@ int main(int argc, char *argv[]) {
 
         test_issue_9549();
         test_multiline();
+        test_strv();
 
         return 0;
 }
index ac8b95aece0eaed479a1d419a67fe8965909c97f..d97ccfda3bcc7c59adaa01aef67fbe649b01a5a9 100644 (file)
@@ -148,6 +148,7 @@ static void test_chase_symlinks(void) {
         r = chase_symlinks(p, NULL, 0, &result, NULL);
         assert_se(r > 0);
         assert_se(path_equal(result, "/usr"));
+        assert_se(streq(result, "/usr")); /* we guarantee that we drop redundant slashes */
         result = mfree(result);
 
         r = chase_symlinks(p, temp, 0, &result, NULL);
@@ -371,6 +372,15 @@ static void test_chase_symlinks(void) {
         assert_se(streq("/usr", result));
         result = mfree(result);
 
+        /* Make sure that symlinks in the "root" path are not resolved, but those below are */
+        p = strjoina("/etc/..", temp, "/self");
+        assert_se(symlink(".", p) >= 0);
+        q = strjoina(p, "/top/dot/dotdota");
+        r = chase_symlinks(q, p, 0, &result, NULL);
+        assert_se(r > 0);
+        assert_se(path_equal(path_startswith(result, p), "usr"));
+        result = mfree(result);
+
  cleanup:
         assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
 }
@@ -729,7 +739,7 @@ static void test_rename_noreplace(void) {
                 STRV_FOREACH(b, (char**) table) {
                         _cleanup_free_ char *w = NULL;
 
-                        w = strjoin(w, *b);
+                        w = strjoin(z, *b);
                         assert_se(w);
 
                         if (access(w, F_OK) < 0) {
@@ -737,7 +747,7 @@ static void test_rename_noreplace(void) {
                                 continue;
                         }
 
-                        assert_se(rename_noreplace(AT_FDCWD, w, AT_FDCWD, y) == -EEXIST);
+                        assert_se(rename_noreplace(AT_FDCWD, x, AT_FDCWD, w) == -EEXIST);
                 }
 
                 y = strjoin(z, "/somethingelse");
@@ -792,6 +802,50 @@ static void test_chmod_and_chown(void) {
         assert_se(S_ISLNK(st.st_mode));
 }
 
+static void test_chmod_and_chown_unsafe(void) {
+        _cleanup_(rm_rf_physical_and_freep) char *d = NULL;
+        _unused_ _cleanup_umask_ mode_t u = umask(0000);
+        struct stat st;
+        const char *p;
+
+        if (geteuid() != 0)
+                return;
+
+        log_info("/* %s */", __func__);
+
+        assert_se(mkdtemp_malloc(NULL, &d) >= 0);
+
+        p = strjoina(d, "/reg");
+        assert_se(mknod(p, S_IFREG | 0123, 0) >= 0);
+
+        assert_se(chmod_and_chown_unsafe(p, S_IFREG | 0321, 1, 2) >= 0);
+        assert_se(chmod_and_chown_unsafe(p, S_IFDIR | 0555, 3, 4) == -EINVAL);
+
+        assert_se(lstat(p, &st) >= 0);
+        assert_se(S_ISREG(st.st_mode));
+        assert_se((st.st_mode & 07777) == 0321);
+
+        p = strjoina(d, "/dir");
+        assert_se(mkdir(p, 0123) >= 0);
+
+        assert_se(chmod_and_chown_unsafe(p, S_IFDIR | 0321, 1, 2) >= 0);
+        assert_se(chmod_and_chown_unsafe(p, S_IFREG | 0555, 3, 4) == -EINVAL);
+
+        assert_se(lstat(p, &st) >= 0);
+        assert_se(S_ISDIR(st.st_mode));
+        assert_se((st.st_mode & 07777) == 0321);
+
+        p = strjoina(d, "/lnk");
+        assert_se(symlink("idontexist", p) >= 0);
+
+        assert_se(chmod_and_chown_unsafe(p, S_IFLNK | 0321, 1, 2) >= 0);
+        assert_se(chmod_and_chown_unsafe(p, S_IFREG | 0555, 3, 4) == -EINVAL);
+        assert_se(chmod_and_chown_unsafe(p, S_IFDIR | 0555, 3, 4) == -EINVAL);
+
+        assert_se(lstat(p, &st) >= 0);
+        assert_se(S_ISLNK(st.st_mode));
+}
+
 int main(int argc, char *argv[]) {
         test_setup_logging(LOG_INFO);
 
@@ -809,6 +863,7 @@ int main(int argc, char *argv[]) {
         test_fsync_directory_of_file();
         test_rename_noreplace();
         test_chmod_and_chown();
+        test_chmod_and_chown_unsafe();
 
         return 0;
 }
index 41ea733b7eb2911e4fd414922a5a98523fb391d4..f2bfc6c62bc9c3f5724e725549cb2932a1bee767 100644 (file)
@@ -148,6 +148,7 @@ static void test_protect_kernel_logs(void) {
                                     NULL, 0,
                                     NULL,
                                     NULL,
+                                    NULL,
                                     PROTECT_HOME_NO,
                                     PROTECT_SYSTEM_NO,
                                     0,
index e9233a16437e23268ec15d3a5e41ad30ee60acb0..cf8b08ba9be55dd5dbbf2c255514e0c622beebc7 100644 (file)
@@ -72,13 +72,14 @@ int main(int argc, char *argv[]) {
                             &(TemporaryFileSystem) { .path = (char*) "/var", .options = (char*) "ro" }, 1,
                             tmp_dir,
                             var_tmp_dir,
+                            NULL,
                             PROTECT_HOME_NO,
                             PROTECT_SYSTEM_NO,
                             0,
                             0,
                             NULL);
         if (r < 0) {
-                log_error_errno(r, "Failed to setup namespace: %m");
+                log_error_errno(r, "Failed to set up namespace: %m");
 
                 log_info("Usage:\n"
                          "  sudo TEST_NS_PROJECTS=/home/lennart/projects ./test-ns\n"
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 4a3f2116588c71049f5cb2d80b7ded2dcebbbb89..d78e0544a7a6236c0f9fcf233e8a4805f4625b4b 100644 (file)
@@ -572,10 +572,8 @@ static void test_pid_to_ptr(void) {
         assert_se(PTR_TO_PID(PID_TO_PTR(INT16_MAX)) == INT16_MAX);
         assert_se(PTR_TO_PID(PID_TO_PTR(INT16_MIN)) == INT16_MIN);
 
-#if SIZEOF_PID_T >= 4
         assert_se(PTR_TO_PID(PID_TO_PTR(INT32_MAX)) == INT32_MAX);
         assert_se(PTR_TO_PID(PID_TO_PTR(INT32_MIN)) == INT32_MIN);
-#endif
 }
 
 static void test_ioprio_class_from_to_string_one(const char *val, int expected) {
index 75566199e3062d23aecf97b120040d038e543525..67900d85e9ac04f692621174692fafafcda3f445 100644 (file)
@@ -190,7 +190,7 @@ static void test_restrict_namespace(void) {
 
         log_info("/* %s */", __func__);
 
-        assert_se(namespace_flags_to_string(0, &s) == 0 && streq(s, ""));
+        assert_se(namespace_flags_to_string(0, &s) == 0 && isempty(s));
         s = mfree(s);
         assert_se(namespace_flags_to_string(CLONE_NEWNS, &s) == 0 && streq(s, "mnt"));
         s = mfree(s);
index 7fc16a62b656c5d497cecdd6c47fc88192dc7972..1020e0cb3153594bcd7f8781e129b82404afc8f2 100644 (file)
@@ -3,6 +3,8 @@
 #include <sched.h>
 #include <stdio.h>
 #include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
 
 #define __STDC_WANT_IEC_60559_TYPES_EXT__
 #include <float.h>
@@ -65,6 +67,7 @@ int main(void) {
         info(pid_t);
         info(uid_t);
         info(gid_t);
+        info(socklen_t);
 
         info(__cpu_mask);
 
index f31ea6f8c61cf6b86efc4c8757ac4509029cb538..dd6233175c092e10a4039ab995041d435448ab95 100644 (file)
@@ -711,27 +711,33 @@ static void test_strv_push(void) {
         assert_se(streq_ptr(a[3], NULL));
 }
 
-static void test_strv_equal(void) {
+static void test_strv_compare(void) {
         _cleanup_strv_free_ char **a = NULL;
         _cleanup_strv_free_ char **b = NULL;
         _cleanup_strv_free_ char **c = NULL;
+        _cleanup_strv_free_ char **d = NULL;
 
         log_info("/* %s */", __func__);
 
         a = strv_new("one", "two", "three");
         assert_se(a);
         b = strv_new("one", "two", "three");
-        assert_se(a);
+        assert_se(b);
         c = strv_new("one", "two", "three", "four");
-        assert_se(a);
+        assert_se(c);
+        d = strv_new(NULL);
+        assert_se(d);
 
-        assert_se(strv_equal(a, a));
-        assert_se(strv_equal(a, b));
-        assert_se(strv_equal(NULL, NULL));
+        assert_se(strv_compare(a, a) == 0);
+        assert_se(strv_compare(a, b) == 0);
+        assert_se(strv_compare(d, d) == 0);
+        assert_se(strv_compare(d, NULL) == 0);
+        assert_se(strv_compare(NULL, NULL) == 0);
 
-        assert_se(!strv_equal(a, c));
-        assert_se(!strv_equal(b, c));
-        assert_se(!strv_equal(b, NULL));
+        assert_se(strv_compare(a, c) < 0);
+        assert_se(strv_compare(b, c) < 0);
+        assert_se(strv_compare(b, d) == 1);
+        assert_se(strv_compare(b, NULL) == 1);
 }
 
 static void test_strv_is_uniq(void) {
@@ -924,14 +930,16 @@ static void test_foreach_string(void) {
 
 static void test_strv_fnmatch(void) {
         _cleanup_strv_free_ char **v = NULL;
+        size_t pos;
 
         log_info("/* %s */", __func__);
 
-        assert_se(!strv_fnmatch(STRV_MAKE_EMPTY, "a", 0));
+        assert_se(!strv_fnmatch(STRV_MAKE_EMPTY, "a"));
 
-        v = strv_new("*\\*");
-        assert_se(!strv_fnmatch(v, "\\", 0));
-        assert_se(strv_fnmatch(v, "\\", FNM_NOESCAPE));
+        v = strv_new("xxx", "*\\*", "yyy");
+        assert_se(!strv_fnmatch_full(v, "\\", 0, NULL));
+        assert_se(strv_fnmatch_full(v, "\\", FNM_NOESCAPE, &pos));
+        assert(pos == 1);
 }
 
 int main(int argc, char *argv[]) {
@@ -988,7 +996,7 @@ int main(int argc, char *argv[]) {
         test_strv_insert();
         test_strv_push_prepend();
         test_strv_push();
-        test_strv_equal();
+        test_strv_compare();
         test_strv_is_uniq();
         test_strv_reverse();
         test_strv_shell_escape();
diff --git a/src/test/test-sysctl-util.c b/src/test/test-sysctl-util.c
new file mode 100644 (file)
index 0000000..2b957dd
--- /dev/null
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "strv.h"
+#include "sysctl-util.h"
+#include "tests.h"
+
+static const char* cases[] = {
+        "a.b.c", "a/b/c",
+        "a/b/c", "a/b/c",
+        "a/b.c/d", "a/b.c/d",
+        "a.b/c.d", "a/b.c/d",
+
+        "net.ipv4.conf.enp3s0/200.forwarding", "net/ipv4/conf/enp3s0.200/forwarding",
+        "net/ipv4/conf/enp3s0.200/forwarding", "net/ipv4/conf/enp3s0.200/forwarding",
+
+        "a...b...c", "a/b/c",
+        "a///b///c", "a/b/c",
+        ".a...b...c", "a/b/c",
+        "/a///b///c", "a/b/c",
+        NULL,
+};
+
+static void test_sysctl_normalize(void) {
+        log_info("/* %s */", __func__);
+
+        const char **s, **expected;
+        STRV_FOREACH_PAIR(s, expected, cases) {
+                _cleanup_free_ char *t;
+
+                assert_se(t = strdup(*s));
+                assert_se(sysctl_normalize(t) == t);
+
+                log_info("\"%s\" → \"%s\", expected \"%s\"", *s, t, *expected);
+                assert_se(streq(t, *expected));
+        }
+}
+
+int main(int argc, char *argv[]) {
+        test_setup_logging(LOG_INFO);
+
+        test_sysctl_normalize();
+
+        return 0;
+}
index fde8336bf9b7a094773d10ec187691f9186d96a1..084a584876dde73ce36971407174c967281e1d57 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "alloc-util.h"
 #include "format-util.h"
+#include "libcrypt-util.h"
 #include "log.h"
 #include "macro.h"
 #include "memory-util.h"
@@ -309,15 +310,19 @@ static void test_gid_lists_ops(void) {
         int nresult;
 
         nresult = merge_gid_lists(l2, ELEMENTSOF(l2), l3, ELEMENTSOF(l3), &res1);
+        assert_se(nresult >= 0);
         assert_se(memcmp_nn(res1, nresult, result1, ELEMENTSOF(result1)) == 0);
 
         nresult = merge_gid_lists(NULL, 0, l2, ELEMENTSOF(l2), &res2);
+        assert_se(nresult >= 0);
         assert_se(memcmp_nn(res2, nresult, l2, ELEMENTSOF(l2)) == 0);
 
         nresult = merge_gid_lists(l1, ELEMENTSOF(l1), l1, ELEMENTSOF(l1), &res3);
+        assert_se(nresult >= 0);
         assert_se(memcmp_nn(l1, ELEMENTSOF(l1), res3, nresult) == 0);
 
         nresult = merge_gid_lists(l1, ELEMENTSOF(l1), l4, ELEMENTSOF(l4), &res4);
+        assert_se(nresult >= 0);
         assert_se(memcmp_nn(result2, ELEMENTSOF(result2), res4, nresult) == 0);
 
         nresult = getgroups_alloc(&gids);
index 4ec3b503592555a3de5e25fd56be4a30e116bf16..5e2fb50d8371b5138f0a2cafc1b1f74d147c1b75 100644 (file)
@@ -12,7 +12,7 @@
 #include "alloc-util.h"
 #include "bus-common-errors.h"
 #include "bus-error.h"
-#include "bus-util.h"
+#include "bus-polkit.h"
 #include "clock-util.h"
 #include "conf-files.h"
 #include "def.h"
index da6b4104767eca24da1ce1dcb1eed37cca215138..e18e1e6c04853597c03dc97cb73ce266ccd543f4 100644 (file)
@@ -110,7 +110,7 @@ static int manager_send_request(Manager *m) {
 
         r = manager_listen_setup(m);
         if (r < 0)
-                return log_warning_errno(r, "Failed to setup connection socket: %m");
+                return log_warning_errno(r, "Failed to set up connection socket: %m");
 
         /*
          * Set transmit timestamp, remember it; the server will send that back
@@ -514,7 +514,7 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
 
         root_distance = ntp_ts_short_to_d(&ntpmsg.root_delay) / 2 + ntp_ts_short_to_d(&ntpmsg.root_dispersion);
         if (root_distance > (double) m->max_root_distance_usec / (double) USEC_PER_SEC) {
-                log_debug("Server has too large root distance. Disconnecting.");
+                log_info("Server has too large root distance. Disconnecting.");
                 return manager_connect(m);
         }
 
index 784dd0df72da769834b5c8cc88aaaf3dfbfe2fc0..0e33c0b48f01564792080235f43a98d692920a87 100644 (file)
@@ -57,17 +57,19 @@ static const char *arg_device = NULL;
 static int send_passwords(const char *socket_name, char **passwords) {
         _cleanup_(erase_and_freep) char *packet = NULL;
         _cleanup_close_ int socket_fd = -1;
-        union sockaddr_union sa = {};
+        union sockaddr_union sa;
+        socklen_t sa_len;
         size_t packet_length = 1;
         char **p, *d;
         ssize_t n;
-        int salen;
+        int r;
 
         assert(socket_name);
 
-        salen = sockaddr_un_set_path(&sa.un, socket_name);
-        if (salen < 0)
-                return salen;
+        r = sockaddr_un_set_path(&sa.un, socket_name);
+        if (r < 0)
+                return r;
+        sa_len = r;
 
         STRV_FOREACH(p, passwords)
                 packet_length += strlen(*p) + 1;
@@ -86,7 +88,7 @@ static int send_passwords(const char *socket_name, char **passwords) {
         if (socket_fd < 0)
                 return log_debug_errno(errno, "socket(): %m");
 
-        n = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, salen);
+        n = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, sa_len);
         if (n < 0)
                 return log_debug_errno(errno, "sendto(): %m");
 
index deeb939f54a17ed6b28e20e2a473abd012f03b56..57389372ce271f9e0c39fe986769f4e239a118ea 100644 (file)
@@ -354,7 +354,7 @@ static int disk_identify(int fd,
             ret = disk_identify_packet_device_command(fd, out_identify, 512);
             goto check_nul_bytes;
           }
-        if (peripheral_device_type != 0x00) {
+        if (!IN_SET(peripheral_device_type, 0x00, 0x14)) {
                 ret = -1;
                 errno = EIO;
                 goto out;
index 686ff1bc5ca9b71e876d8f57868bd5f34c2c9b76..2784246dd7155547d175f74b58af4704d8deb649 100644 (file)
@@ -40,11 +40,13 @@ Link.AlternativeName,            config_parse_ifnames,                  1,
 Link.AlternativeNamesPolicy,     config_parse_alternative_names_policy, 0,                             offsetof(link_config, alternative_names_policy)
 Link.Alias,                      config_parse_ifalias,                  0,                             offsetof(link_config, alias)
 Link.MTUBytes,                   config_parse_mtu,                      AF_UNSPEC,                     offsetof(link_config, mtu)
-Link.BitsPerSecond,              config_parse_si_size,                  0,                             offsetof(link_config, speed)
+Link.BitsPerSecond,              config_parse_si_uint64,                0,                             offsetof(link_config, speed)
 Link.Duplex,                     config_parse_duplex,                   0,                             offsetof(link_config, duplex)
 Link.AutoNegotiation,            config_parse_tristate,                 0,                             offsetof(link_config, autonegotiation)
 Link.WakeOnLan,                  config_parse_wol,                      0,                             offsetof(link_config, wol)
 Link.Port,                       config_parse_port,                     0,                             offsetof(link_config, port)
+Link.ReceiveChecksumOffload,     config_parse_tristate,                 0,                             offsetof(link_config, features[NET_DEV_FEAT_RX])
+Link.TransmitChecksumOffload,    config_parse_tristate,                 0,                             offsetof(link_config, features[NET_DEV_FEAT_TX])
 Link.GenericSegmentationOffload, config_parse_tristate,                 0,                             offsetof(link_config, features[NET_DEV_FEAT_GSO])
 Link.TCPSegmentationOffload,     config_parse_tristate,                 0,                             offsetof(link_config, features[NET_DEV_FEAT_TSO])
 Link.TCP6SegmentationOffload,    config_parse_tristate,                 0,                             offsetof(link_config, features[NET_DEV_FEAT_TSO6])
@@ -58,3 +60,6 @@ Link.CombinedChannels,           config_parse_channel,                  0,
 Link.Advertise,                  config_parse_advertise,                0,                             offsetof(link_config, advertise)
 Link.RxBufferSize,               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 4a44edfc014dc83bf66e7f9f02f8b419178556fc..9c82759818a2875bfb4b24b333da7d072aa09847 100644 (file)
@@ -148,6 +148,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++)
@@ -160,16 +163,14 @@ int link_load_one(link_config_ctx *ctx, const char *filename) {
         if (r < 0)
                 return r;
 
-        if (link->speed > UINT_MAX)
-                return -ERANGE;
-
         if (set_isempty(link->match_mac) && set_isempty(link->match_permanent_mac) &&
             strv_isempty(link->match_path) && strv_isempty(link->match_driver) && strv_isempty(link->match_type) &&
-            strv_isempty(link->match_name) && strv_isempty(link->match_property) && !link->conditions)
-                log_warning("%s: No valid settings found in the [Match] section. "
-                            "The file will match all interfaces. "
-                            "If that is intended, please add OriginalName=* in the [Match] section.",
+            strv_isempty(link->match_name) && strv_isempty(link->match_property) && !link->conditions) {
+                log_warning("%s: No valid settings found in the [Match] section, ignoring file. "
+                            "To match all interfaces, add OriginalName=* in the [Match] section.",
                             filename);
+                return 0;
+        }
 
         if (!condition_test_list(link->conditions, NULL, NULL, NULL)) {
                 log_debug("%s: Conditions do not match the system environment, skipping.", filename);
@@ -238,9 +239,10 @@ bool link_config_should_reload(link_config_ctx *ctx) {
 
 int link_config_get(link_config_ctx *ctx, sd_device *device, link_config **ret) {
         struct ether_addr permanent_mac = {};
+        unsigned short iftype = 0;
         link_config *link;
         const char *name;
-        int r;
+        int ifindex, r;
 
         assert(ctx);
         assert(device);
@@ -250,6 +252,14 @@ int link_config_get(link_config_ctx *ctx, sd_device *device, link_config **ret)
         if (r < 0)
                 return r;
 
+        r = sd_device_get_ifindex(device, &ifindex);
+        if (r < 0)
+                return r;
+
+        r = rtnl_get_link_iftype(&ctx->rtnl, ifindex, &iftype);
+        if (r < 0)
+                return r;
+
         r = ethtool_get_permanent_macaddr(&ctx->ethtool_fd, name, &permanent_mac);
         if (r < 0)
                 log_device_debug_errno(device, r, "Failed to get permanent MAC address, ignoring: %m");
@@ -257,7 +267,7 @@ int link_config_get(link_config_ctx *ctx, sd_device *device, link_config **ret)
         LIST_FOREACH(links, link, ctx->links) {
                 if (net_match_config(link->match_mac, link->match_permanent_mac, link->match_path, link->match_driver,
                                      link->match_type, link->match_name, link->match_property, NULL, NULL, NULL,
-                                     device, NULL, &permanent_mac, NULL, NULL, 0, NULL, NULL)) {
+                                     iftype, device, NULL, &permanent_mac, NULL, NULL, 0, NULL, NULL)) {
                         if (link->match_name && !strv_contains(link->match_name, "*")) {
                                 unsigned name_assign_type = NET_NAME_UNKNOWN;
 
@@ -402,6 +412,10 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
                         log_warning_errno(r, "Could not set ring buffer of %s: %m", old_name);
         }
 
+        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");
index 496a8bccb78c38a5928cae7e640d60f6f3ee2e57..827ebf436c715bb12bcdc774807879ca7139a9a1 100644 (file)
@@ -53,7 +53,7 @@ struct link_config {
         char **alternative_names;
         char *alias;
         uint32_t mtu;
-        size_t speed;
+        uint64_t speed;
         Duplex duplex;
         int autonegotiation;
         uint32_t advertise[N_ADVERTISE];
@@ -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 36cda504e728589e8851f32e7c18e328615a9b53..8e86d2f0d1c01fbd89e3a21b97cebb4c0834de54 100644 (file)
@@ -163,7 +163,7 @@ static int builtin_hwdb(sd_device *dev, int argc, char *argv[], bool test) {
         if (argv[optind]) {
                 r = udev_builtin_hwdb_lookup(dev, prefix, argv[optind], filter, test);
                 if (r < 0)
-                        return log_device_debug_errno(dev, r, "Failed to lookup hwdb: %m");
+                        return log_device_debug_errno(dev, r, "Failed to look up hwdb: %m");
                 if (r == 0)
                         return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ENODATA), "No entry found from hwdb.");
                 return r;
@@ -178,7 +178,7 @@ static int builtin_hwdb(sd_device *dev, int argc, char *argv[], bool test) {
 
         r = udev_builtin_hwdb_search(dev, srcdev, subsystem, prefix, filter, test);
         if (r < 0)
-                return log_device_debug_errno(dev, r, "Failed to lookup hwdb: %m");
+                return log_device_debug_errno(dev, r, "Failed to look up hwdb: %m");
         if (r == 0)
                 return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ENODATA), "No entry found from hwdb.");
         return r;
index 840bd01e1b2dbbc344a7e57fb183de9cf0c380d0..9ff2b02289892d408f514265c1d66a0f35ec19c4 100644 (file)
@@ -16,6 +16,7 @@
 #include "device-util.h"
 #include "fd-util.h"
 #include "missing_input.h"
+#include "parse-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
 #include "udev-builtin.h"
@@ -123,8 +124,25 @@ static void get_cap_mask(sd_device *pdev, const char* attr,
         }
 }
 
+static struct input_id get_input_id(sd_device *dev) {
+        const char *v;
+        struct input_id id = {};
+
+        if (sd_device_get_sysattr_value(dev, "id/bustype", &v) >= 0)
+                (void) safe_atoux16(v, &id.bustype);
+        if (sd_device_get_sysattr_value(dev, "id/vendor", &v) >= 0)
+                (void) safe_atoux16(v, &id.vendor);
+        if (sd_device_get_sysattr_value(dev, "id/product", &v) >= 0)
+                (void) safe_atoux16(v, &id.product);
+        if (sd_device_get_sysattr_value(dev, "id/version", &v) >= 0)
+                (void) safe_atoux16(v, &id.version);
+
+        return id;
+}
+
 /* pointer devices */
 static bool test_pointers(sd_device *dev,
+                          const struct input_id *id,
                           const unsigned long* bitmask_ev,
                           const unsigned long* bitmask_abs,
                           const unsigned long* bitmask_key,
@@ -149,7 +167,7 @@ static bool test_pointers(sd_device *dev,
         bool is_tablet = false;
         bool is_joystick = false;
         bool is_accelerometer = false;
-        bool is_pointing_stick= false;
+        bool is_pointing_stick = false;
 
         has_keys = test_bit(EV_KEY, bitmask_ev);
         has_abs_coordinates = test_bit(ABS_X, bitmask_abs) && test_bit(ABS_Y, bitmask_abs);
@@ -229,6 +247,10 @@ static bool test_pointers(sd_device *dev,
             !has_abs_coordinates)) /* mouse buttons and no axis */
                 is_mouse = true;
 
+        /* There is no such thing as an i2c mouse */
+        if (is_mouse && id->bustype == BUS_I2C)
+                is_pointing_stick = true;
+
         if (is_pointing_stick)
                 udev_builtin_add_property(dev, test, "ID_INPUT_POINTINGSTICK", "1");
         if (is_mouse)
@@ -326,6 +348,8 @@ static int builtin_input_id(sd_device *dev, int argc, char *argv[], bool test) {
         }
 
         if (pdev) {
+                struct input_id id = get_input_id(pdev);
+
                 /* Use this as a flag that input devices were detected, so that this
                  * program doesn't need to be called more than once per device */
                 udev_builtin_add_property(dev, test, "ID_INPUT", "1");
@@ -334,7 +358,7 @@ static int builtin_input_id(sd_device *dev, int argc, char *argv[], bool test) {
                 get_cap_mask(pdev, "capabilities/rel", bitmask_rel, sizeof(bitmask_rel), test);
                 get_cap_mask(pdev, "capabilities/key", bitmask_key, sizeof(bitmask_key), test);
                 get_cap_mask(pdev, "properties", bitmask_props, sizeof(bitmask_props), test);
-                is_pointer = test_pointers(dev, bitmask_ev, bitmask_abs,
+                is_pointer = test_pointers(dev, &id, bitmask_ev, bitmask_abs,
                                            bitmask_key, bitmask_rel,
                                            bitmask_props, test);
                 is_key = test_key(dev, bitmask_ev, bitmask_key, test);
index b9b350d1ef7a4f8064a7a7bd1e6b796d4bfaff7a..b990f68e933c19933290c2e08d402a8e7cf260c0 100644 (file)
@@ -921,7 +921,7 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
                         op = OP_ASSIGN;
                 }
 
-                r = rule_line_add_token(rule_line, TK_A_SECLABEL, op, value, NULL);
+                r = rule_line_add_token(rule_line, TK_A_SECLABEL, op, value, attr);
         } else if (streq(key, "RUN")) {
                 if (is_match || op == OP_REMOVE)
                         return log_token_invalid_op(rules, key);
index 2d2bc0026e8dfbe4f34f5974290bae1b857a8e7b..1debdf2b314cd4ecd213002191defc7db1368088 100644 (file)
@@ -177,18 +177,18 @@ static int export_devices(void) {
 
         r = sd_device_enumerator_new(&e);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = sd_device_enumerator_allow_uninitialized(e);
         if (r < 0)
-                return r;
+                return log_error_errno(r, "Failed to set allowing uninitialized flag: %m");
 
         r = device_enumerator_scan_devices(e);
         if (r < 0)
-                return r;
+                return log_error_errno(r, "Failed to scan devices: %m");
 
         FOREACH_DEVICE_AND_SUBSYSTEM(e, d)
-                print_record(d);
+                (void) print_record(d);
 
         return 0;
 }
index 7678331897f5e988a66ef2dba903433fae518091..456bc8c479a39e0bd268e849748f1ef2c433b5be 100644 (file)
@@ -559,6 +559,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;
@@ -770,6 +778,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;
 }
@@ -1577,7 +1588,11 @@ static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cg
         if (r < 0)
                 return log_error_errno(r, "Failed to initialize device monitor: %m");
 
-        (void) sd_device_monitor_set_receive_buffer_size(manager->monitor, 128 * 1024 * 1024);
+        /* Bump receiver buffer, but only if we are not called via socket activation, as in that
+         * case systemd sets the receive buffer size for us, and the value in the .socket unit
+         * should take full effect. */
+        if (fd_uevent < 0)
+                (void) sd_device_monitor_set_receive_buffer_size(manager->monitor, 128 * 1024 * 1024);
 
         r = device_monitor_enable_receiving(manager->monitor);
         if (r < 0)
diff --git a/src/userdb/meson.build b/src/userdb/meson.build
new file mode 100644 (file)
index 0000000..2f7e1ac
--- /dev/null
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: LGPL-2.1+
+
+systemd_userwork_sources = files('''
+        userwork.c
+'''.split())
+
+systemd_userdbd_sources = files('''
+        userdbd-manager.c
+        userdbd-manager.h
+        userdbd.c
+'''.split())
+
+userdbctl_sources = files('''
+        userdbctl.c
+'''.split())
diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c
new file mode 100644 (file)
index 0000000..b3ddd9d
--- /dev/null
@@ -0,0 +1,792 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <getopt.h>
+#include <utmp.h>
+
+#include "dirent-util.h"
+#include "errno-list.h"
+#include "fd-util.h"
+#include "format-table.h"
+#include "format-util.h"
+#include "group-record-show.h"
+#include "main-func.h"
+#include "pager.h"
+#include "parse-util.h"
+#include "pretty-print.h"
+#include "socket-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "user-record-show.h"
+#include "user-util.h"
+#include "userdb.h"
+#include "verbs.h"
+
+static enum {
+        OUTPUT_CLASSIC,
+        OUTPUT_TABLE,
+        OUTPUT_FRIENDLY,
+        OUTPUT_JSON,
+        _OUTPUT_INVALID = -1
+} arg_output = _OUTPUT_INVALID;
+
+static PagerFlags arg_pager_flags = 0;
+static bool arg_legend = true;
+static char** arg_services = NULL;
+static UserDBFlags arg_userdb_flags = 0;
+
+STATIC_DESTRUCTOR_REGISTER(arg_services, strv_freep);
+
+static int show_user(UserRecord *ur, Table *table) {
+        int r;
+
+        assert(ur);
+
+        switch (arg_output) {
+
+        case OUTPUT_CLASSIC:
+                if (!uid_is_valid(ur->uid))
+                        break;
+
+                printf("%s:x:" UID_FMT ":" GID_FMT ":%s:%s:%s\n",
+                       ur->user_name,
+                       ur->uid,
+                       user_record_gid(ur),
+                       strempty(user_record_real_name(ur)),
+                       user_record_home_directory(ur),
+                       user_record_shell(ur));
+
+                break;
+
+        case OUTPUT_JSON:
+                json_variant_dump(ur->json, JSON_FORMAT_COLOR_AUTO|JSON_FORMAT_PRETTY, NULL, 0);
+                break;
+
+        case OUTPUT_FRIENDLY:
+                user_record_show(ur, true);
+
+                if (ur->incomplete) {
+                        fflush(stdout);
+                        log_warning("Warning: lacking rights to acquire privileged fields of user record of '%s', output incomplete.", ur->user_name);
+                }
+
+                break;
+
+        case OUTPUT_TABLE:
+                assert(table);
+
+                r = table_add_many(
+                                table,
+                                TABLE_STRING, ur->user_name,
+                                TABLE_STRING, user_disposition_to_string(user_record_disposition(ur)),
+                                TABLE_UID, ur->uid,
+                                TABLE_GID, user_record_gid(ur),
+                                TABLE_STRING, empty_to_null(ur->real_name),
+                                TABLE_STRING, user_record_home_directory(ur),
+                                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");
+
+                break;
+
+        default:
+                assert_not_reached("Unexpected output mode");
+        }
+
+        return 0;
+}
+
+static int display_user(int argc, char *argv[], void *userdata) {
+        _cleanup_(table_unrefp) Table *table = NULL;
+        bool draw_separator = false;
+        int ret = 0, r;
+
+        if (arg_output < 0)
+                arg_output = argc > 1 ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
+
+        if (arg_output == OUTPUT_TABLE) {
+                table = table_new("name", "disposition", "uid", "gid", "realname", "home", "shell", "disposition-numeric");
+                if (!table)
+                        return log_oom();
+
+                (void) table_set_align_percent(table, table_get_cell(table, 0, 2), 100);
+                (void) table_set_align_percent(table, table_get_cell(table, 0, 3), 100);
+                (void) table_set_empty_string(table, "-");
+                (void) table_set_sort(table, (size_t) 7, (size_t) 2, (size_t) -1);
+                (void) table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4, (size_t) 5, (size_t) 6, (size_t) -1);
+        }
+
+        if (argc > 1) {
+                char **i;
+
+                STRV_FOREACH(i, argv + 1) {
+                        _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+                        uid_t uid;
+
+                        if (parse_uid(*i, &uid) >= 0)
+                                r = userdb_by_uid(uid, arg_userdb_flags, &ur);
+                        else
+                                r = userdb_by_name(*i, arg_userdb_flags, &ur);
+                        if (r < 0) {
+                                if (r == -ESRCH)
+                                        log_error_errno(r, "User %s does not exist.", *i);
+                                else if (r == -EHOSTDOWN)
+                                        log_error_errno(r, "Selected user database service is not available for this request.");
+                                else
+                                        log_error_errno(r, "Failed to find user %s: %m", *i);
+
+                                if (ret >= 0)
+                                        ret = r;
+                        } else {
+                                if (draw_separator && arg_output == OUTPUT_FRIENDLY)
+                                        putchar('\n');
+
+                                r = show_user(ur, table);
+                                if (r < 0)
+                                        return r;
+
+                                draw_separator = true;
+                        }
+                }
+        } else {
+                _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+
+                r = userdb_all(arg_userdb_flags, &iterator);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to enumerate users: %m");
+
+                for (;;) {
+                        _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+
+                        r = userdb_iterator_get(iterator, &ur);
+                        if (r == -ESRCH)
+                                break;
+                        if (r == -EHOSTDOWN)
+                                return log_error_errno(r, "Selected user database service is not available for this request.");
+                        if (r < 0)
+                                return log_error_errno(r, "Failed acquire next user: %m");
+
+                        if (draw_separator && arg_output == OUTPUT_FRIENDLY)
+                                putchar('\n');
+
+                        r = show_user(ur, table);
+                        if (r < 0)
+                                return r;
+
+                        draw_separator = true;
+                }
+        }
+
+        if (table) {
+                r = table_print(table, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to show table: %m");
+        }
+
+        return ret;
+}
+
+static int show_group(GroupRecord *gr, Table *table) {
+        int r;
+
+        assert(gr);
+
+        switch (arg_output) {
+
+        case OUTPUT_CLASSIC: {
+                _cleanup_free_ char *m = NULL;
+
+                if (!gid_is_valid(gr->gid))
+                        break;
+
+                m = strv_join(gr->members, ",");
+                if (!m)
+                        return log_oom();
+
+                printf("%s:x:" GID_FMT ":%s\n",
+                       gr->group_name,
+                       gr->gid,
+                       m);
+                break;
+        }
+
+        case OUTPUT_JSON:
+                json_variant_dump(gr->json, JSON_FORMAT_COLOR_AUTO|JSON_FORMAT_PRETTY, NULL, 0);
+                break;
+
+        case OUTPUT_FRIENDLY:
+                group_record_show(gr, true);
+
+                if (gr->incomplete) {
+                        fflush(stdout);
+                        log_warning("Warning: lacking rights to acquire privileged fields of group record of '%s', output incomplete.", gr->group_name);
+                }
+
+                break;
+
+        case OUTPUT_TABLE:
+                assert(table);
+
+                r = table_add_many(
+                                table,
+                                TABLE_STRING, gr->group_name,
+                                TABLE_STRING, user_disposition_to_string(group_record_disposition(gr)),
+                                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");
+
+                break;
+
+        default:
+                assert_not_reached("Unexpected disply mode");
+        }
+
+        return 0;
+}
+
+
+static int display_group(int argc, char *argv[], void *userdata) {
+        _cleanup_(table_unrefp) Table *table = NULL;
+        bool draw_separator = false;
+        int ret = 0, r;
+
+        if (arg_output < 0)
+                arg_output = argc > 1 ? OUTPUT_FRIENDLY : OUTPUT_TABLE;
+
+        if (arg_output == OUTPUT_TABLE) {
+                table = table_new("name", "disposition", "gid", "disposition-numeric");
+                if (!table)
+                        return log_oom();
+
+                (void) table_set_align_percent(table, table_get_cell(table, 0, 2), 100);
+                (void) table_set_sort(table, (size_t) 3, (size_t) 2, (size_t) -1);
+                (void) table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) -1);
+        }
+
+        if (argc > 1) {
+                char **i;
+
+                STRV_FOREACH(i, argv + 1) {
+                        _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
+                        gid_t gid;
+
+                        if (parse_gid(*i, &gid) >= 0)
+                                r = groupdb_by_gid(gid, arg_userdb_flags, &gr);
+                        else
+                                r = groupdb_by_name(*i, arg_userdb_flags, &gr);
+                        if (r < 0) {
+                                if (r == -ESRCH)
+                                        log_error_errno(r, "Group %s does not exist.", *i);
+                                else if (r == -EHOSTDOWN)
+                                        log_error_errno(r, "Selected group database service is not available for this request.");
+                                else
+                                        log_error_errno(r, "Failed to find group %s: %m", *i);
+
+                                if (ret >= 0)
+                                        ret = r;
+                        } else {
+                                if (draw_separator && arg_output == OUTPUT_FRIENDLY)
+                                        putchar('\n');
+
+                                r = show_group(gr, table);
+                                if (r < 0)
+                                        return r;
+
+                                draw_separator = true;
+                        }
+                }
+
+        } else {
+                _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+
+                r = groupdb_all(arg_userdb_flags, &iterator);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to enumerate groups: %m");
+
+                for (;;) {
+                        _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
+
+                        r = groupdb_iterator_get(iterator, &gr);
+                        if (r == -ESRCH)
+                                break;
+                        if (r == -EHOSTDOWN)
+                                return log_error_errno(r, "Selected group database service is not available for this request.");
+                        if (r < 0)
+                                return log_error_errno(r, "Failed acquire next group: %m");
+
+                        if (draw_separator && arg_output == OUTPUT_FRIENDLY)
+                                putchar('\n');
+
+                        r = show_group(gr, table);
+                        if (r < 0)
+                                return r;
+
+                        draw_separator = true;
+                }
+
+        }
+
+        if (table) {
+                r = table_print(table, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to show table: %m");
+        }
+
+        return ret;
+}
+
+static int show_membership(const char *user, const char *group, Table *table) {
+        int r;
+
+        assert(user);
+        assert(group);
+
+        switch (arg_output) {
+
+        case OUTPUT_CLASSIC:
+                /* Strictly speaking there's no 'classic' output for this concept, but let's output it in
+                 * similar style to the classic output for user/group info */
+
+                printf("%s:%s\n", user, group);
+                break;
+
+        case OUTPUT_JSON: {
+                _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+                r = json_build(&v, JSON_BUILD_OBJECT(
+                                               JSON_BUILD_PAIR("user", JSON_BUILD_STRING(user)),
+                                               JSON_BUILD_PAIR("group", JSON_BUILD_STRING(group))));
+                if (r < 0)
+                        return log_error_errno(r, "Failed to build JSON object: %m");
+
+                json_variant_dump(v, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR_AUTO, NULL, NULL);
+                break;
+        }
+
+        case OUTPUT_FRIENDLY:
+                /* Hmm, this is not particularly friendly, but not sure how we could do this better */
+                printf("%s: %s\n", group, user);
+                break;
+
+        case OUTPUT_TABLE:
+                assert(table);
+
+                r = table_add_many(
+                                table,
+                                TABLE_STRING, user,
+                                TABLE_STRING, group);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add row to table: %m");
+
+                break;
+
+        default:
+                assert_not_reached("Unexpected output mode");
+        }
+
+        return 0;
+}
+
+static int display_memberships(int argc, char *argv[], void *userdata) {
+        _cleanup_(table_unrefp) Table *table = NULL;
+        int ret = 0, r;
+
+        if (arg_output < 0)
+                arg_output = OUTPUT_TABLE;
+
+        if (arg_output == OUTPUT_TABLE) {
+                table = table_new("user", "group");
+                if (!table)
+                        return log_oom();
+
+                (void) table_set_sort(table, (size_t) 0, (size_t) 1, (size_t) -1);
+        }
+
+        if (argc > 1) {
+                char **i;
+
+                STRV_FOREACH(i, argv + 1) {
+                        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+
+                        if (streq(argv[0], "users-in-group")) {
+                                r = membershipdb_by_group(*i, arg_userdb_flags, &iterator);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to enumerate users in group: %m");
+                        } else if (streq(argv[0], "groups-of-user")) {
+                                r = membershipdb_by_user(*i, arg_userdb_flags, &iterator);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to enumerate groups of user: %m");
+                        } else
+                                assert_not_reached("Unexpected verb");
+
+                        for (;;) {
+                                _cleanup_free_ char *user = NULL, *group = NULL;
+
+                                r = membershipdb_iterator_get(iterator, &user, &group);
+                                if (r == -ESRCH)
+                                        break;
+                                if (r == -EHOSTDOWN)
+                                        return log_error_errno(r, "Selected membership database service is not available for this request.");
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed acquire next membership: %m");
+
+                                r = show_membership(user, group, table);
+                                if (r < 0)
+                                        return r;
+                        }
+                }
+        } else {
+                _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+
+                r = membershipdb_all(arg_userdb_flags, &iterator);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to enumerate memberships: %m");
+
+                for (;;) {
+                        _cleanup_free_ char *user = NULL, *group = NULL;
+
+                        r = membershipdb_iterator_get(iterator, &user, &group);
+                        if (r == -ESRCH)
+                                break;
+                        if (r == -EHOSTDOWN)
+                                return log_error_errno(r, "Selected membership database service is not available for this request.");
+                        if (r < 0)
+                                return log_error_errno(r, "Failed acquire next membership: %m");
+
+                        r = show_membership(user, group, table);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        if (table) {
+                r = table_print(table, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to show table: %m");
+        }
+
+        return ret;
+}
+
+static int display_services(int argc, char *argv[], void *userdata) {
+        _cleanup_(table_unrefp) Table *t = NULL;
+        _cleanup_(closedirp) DIR *d = NULL;
+        struct dirent *de;
+        int r;
+
+        d = opendir("/run/systemd/userdb/");
+        if (!d) {
+                if (errno == ENOENT) {
+                        log_info("No services.");
+                        return 0;
+                }
+
+                return log_error_errno(errno, "Failed to open /run/systemd/userdb/: %m");
+        }
+
+        t = table_new("service", "listening");
+        if (!t)
+                return log_oom();
+
+        (void) table_set_sort(t, (size_t) 0, (size_t) -1);
+
+        FOREACH_DIRENT(de, d, return -errno) {
+                _cleanup_free_ char *j = NULL, *no = NULL;
+                union sockaddr_union sockaddr;
+                socklen_t sockaddr_len;
+                _cleanup_close_ int fd = -1;
+
+                j = path_join("/run/systemd/userdb/", de->d_name);
+                if (!j)
+                        return log_oom();
+
+                r = sockaddr_un_set_path(&sockaddr.un, j);
+                if (r < 0)
+                        return log_error_errno(r, "Path %s does not fit in AF_UNIX socket address: %m", j);
+                sockaddr_len = r;
+
+                fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+                if (fd < 0)
+                        return log_error_errno(r, "Failed to allocate AF_UNIX/SOCK_STREAM socket: %m");
+
+                if (connect(fd, &sockaddr.un, sockaddr_len) < 0) {
+                        no = strjoin("No (", errno_to_name(errno), ")");
+                        if (!no)
+                                return log_oom();
+                }
+
+                r = table_add_many(t,
+                                   TABLE_STRING, de->d_name,
+                                   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");
+        }
+
+        if (table_get_rows(t) <= 0) {
+                log_info("No services.");
+                return 0;
+        }
+
+        if (arg_output == OUTPUT_JSON)
+                table_print_json(t, NULL, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR_AUTO);
+        else
+                table_print(t, NULL);
+
+        return 0;
+}
+
+static int ssh_authorized_keys(int argc, char *argv[], void *userdata) {
+        _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+        int r;
+
+        if (!valid_user_group_name(argv[1]))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid user name '%s'.", argv[1]);
+
+        r = userdb_by_name(argv[1], arg_userdb_flags, &ur);
+        if (r == -ESRCH)
+                log_error_errno(r, "User %s does not exist.", argv[1]);
+        else if (r == -EHOSTDOWN)
+                log_error_errno(r, "Selected user database service is not available for this request.");
+        else if (r < 0)
+                log_error_errno(r, "Failed to find user %s: %m", argv[1]);
+
+        if (strv_isempty(ur->ssh_authorized_keys))
+                log_debug("User record for %s has no public SSH keys.", argv[1]);
+        else {
+                char **i;
+
+                STRV_FOREACH(i, ur->ssh_authorized_keys)
+                        printf("%s\n", *i);
+        }
+
+        if (ur->incomplete) {
+                fflush(stdout);
+                log_warning("Warning: lacking rights to acquire privileged fields of user record of '%s', output incomplete.", ur->user_name);
+        }
+
+        return EXIT_SUCCESS;
+}
+
+static int help(int argc, char *argv[], void *userdata) {
+        _cleanup_free_ char *link = NULL;
+        int r;
+
+        (void) pager_open(arg_pager_flags);
+
+        r = terminal_urlify_man("userdbctl", "1", &link);
+        if (r < 0)
+                return log_oom();
+
+        printf("%s [OPTIONS...] COMMAND ...\n\n"
+               "%sShow user and group information.%s\n"
+               "\nCommands:\n"
+               "  user [USER…]                Inspect user\n"
+               "  group [GROUP…]              Inspect group\n"
+               "  users-in-group [GROUP…]     Show users that are members of specified group(s)\n"
+               "  groups-of-user [USER…]      Show groups the specified user(s) is a member of\n"
+               "  services                    Show enabled database services\n"
+               "\nOptions:\n"
+               "  -h --help                   Show this help\n"
+               "     --version                Show package version\n"
+               "     --no-pager               Do not pipe output into a pager\n"
+               "     --no-legend              Do not show the headers and footers\n"
+               "     --output=MODE            Select output mode (classic, friendly, table, json)\n"
+               "  -j                          Equivalent to --output=json\n"
+               "  -s --service=SERVICE[:SERVICE…]\n"
+               "                              Query the specified service\n"
+               "     --with-nss=BOOL          Control whether to include glibc NSS data\n"
+               "  -N                          Disable inclusion of glibc NSS data and disable synthesizing\n"
+               "                              (Same as --with-nss=no --synthesize=no)\n"
+               "     --synthesize=BOOL        Synthesize root/nobody user\n"
+               "\nSee the %s for details.\n"
+               , program_invocation_short_name
+               , ansi_highlight(), ansi_normal()
+               , link
+        );
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_VERSION = 0x100,
+                ARG_NO_PAGER,
+                ARG_NO_LEGEND,
+                ARG_OUTPUT,
+                ARG_WITH_NSS,
+                ARG_SYNTHESIZE,
+        };
+
+        static const struct option options[] = {
+                { "help",       no_argument,       NULL, 'h'            },
+                { "version",    no_argument,       NULL, ARG_VERSION    },
+                { "no-pager",   no_argument,       NULL, ARG_NO_PAGER   },
+                { "no-legend",  no_argument,       NULL, ARG_NO_LEGEND  },
+                { "output",     required_argument, NULL, ARG_OUTPUT     },
+                { "service",    required_argument, NULL, 's'            },
+                { "with-nss",   required_argument, NULL, ARG_WITH_NSS   },
+                { "synthesize", required_argument, NULL, ARG_SYNTHESIZE },
+                {}
+        };
+
+        const char *e;
+        int r;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        /* We are going to update this environment variable with our own, hence let's first read what is already set */
+        e = getenv("SYSTEMD_ONLY_USERDB");
+        if (e) {
+                char **l;
+
+                l = strv_split(e, ":");
+                if (!l)
+                        return log_oom();
+
+                strv_free(arg_services);
+                arg_services = l;
+        }
+
+        for (;;) {
+                int c;
+
+                c = getopt_long(argc, argv, "hjs:N", options, NULL);
+                if (c < 0)
+                        break;
+
+                switch (c) {
+
+                case 'h':
+                        return help(0, NULL, NULL);
+
+                case ARG_VERSION:
+                        return version();
+
+                case ARG_NO_PAGER:
+                        arg_pager_flags |= PAGER_DISABLE;
+                        break;
+
+                case ARG_NO_LEGEND:
+                        arg_legend = false;
+                        break;
+
+                case ARG_OUTPUT:
+                        if (streq(optarg, "classic"))
+                                arg_output = OUTPUT_CLASSIC;
+                        else if (streq(optarg, "friendly"))
+                                arg_output = OUTPUT_FRIENDLY;
+                        else if (streq(optarg, "json"))
+                                arg_output = OUTPUT_JSON;
+                        else if (streq(optarg, "table"))
+                                arg_output = OUTPUT_TABLE;
+                        else if (streq(optarg, "help")) {
+                                puts("classic\n"
+                                     "friendly\n"
+                                     "json");
+                                return 0;
+                        } else
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid --output= mode: %s", optarg);
+
+                        break;
+
+                case 'j':
+                        arg_output = OUTPUT_JSON;
+                        break;
+
+                case 's':
+                        if (isempty(optarg))
+                                arg_services = strv_free(arg_services);
+                        else {
+                                _cleanup_strv_free_ char **l = NULL;
+
+                                l = strv_split(optarg, ":");
+                                if (!l)
+                                        return log_oom();
+
+                                r = strv_extend_strv(&arg_services, l, true);
+                                if (r < 0)
+                                        return log_oom();
+                        }
+
+                        break;
+
+                case 'N':
+                        arg_userdb_flags |= USERDB_AVOID_NSS|USERDB_DONT_SYNTHESIZE;
+                        break;
+
+                case ARG_WITH_NSS:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --with-nss= parameter: %s", optarg);
+
+                        SET_FLAG(arg_userdb_flags, USERDB_AVOID_NSS, !r);
+                        break;
+
+                case ARG_SYNTHESIZE:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --synthesize= parameter: %s", optarg);
+
+                        SET_FLAG(arg_userdb_flags, USERDB_DONT_SYNTHESIZE, !r);
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        assert_not_reached("Unhandled option");
+                }
+        }
+
+        return 1;
+}
+
+static int run(int argc, char *argv[]) {
+        static const Verb verbs[] = {
+                { "help",                VERB_ANY, VERB_ANY, 0,            help                },
+                { "user",                VERB_ANY, VERB_ANY, VERB_DEFAULT, display_user        },
+                { "group",               VERB_ANY, VERB_ANY, 0,            display_group       },
+                { "users-in-group",      VERB_ANY, VERB_ANY, 0,            display_memberships },
+                { "groups-of-user",      VERB_ANY, VERB_ANY, 0,            display_memberships },
+                { "services",            VERB_ANY, 1,        0,            display_services    },
+
+                /* This one is a helper for sshd_config's AuthorizedKeysCommand= setting, it's not a
+                 * user-facing verb and thus should not appear in man pages or --help texts. */
+                { "ssh-authorized-keys", 2,        2,        0,            ssh_authorized_keys },
+                {}
+        };
+
+        int r;
+
+        log_show_color(true);
+        log_parse_environment();
+        log_open();
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r;
+
+        if (arg_services) {
+                _cleanup_free_ char *e = NULL;
+
+                e = strv_join(arg_services, ":");
+                if (!e)
+                        return log_oom();
+
+                if (setenv("SYSTEMD_ONLY_USERDB", e, true) < 0)
+                        return log_error_errno(r, "Failed to set $SYSTEMD_ONLY_USERDB: %m");
+
+                log_info("Enabled services: %s", e);
+        } else {
+                if (unsetenv("SYSTEMD_ONLY_USERDB") < 0)
+                        return log_error_errno(r, "Failed to unset $SYSTEMD_ONLY_USERDB: %m");
+        }
+
+        return dispatch_verb(argc, argv, verbs, NULL);
+}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/userdb/userdbd-manager.c b/src/userdb/userdbd-manager.c
new file mode 100644 (file)
index 0000000..0602857
--- /dev/null
@@ -0,0 +1,301 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/wait.h>
+
+#include "sd-daemon.h"
+
+#include "fd-util.h"
+#include "fs-util.h"
+#include "mkdir.h"
+#include "process-util.h"
+#include "set.h"
+#include "signal-util.h"
+#include "socket-util.h"
+#include "stdio-util.h"
+#include "umask-util.h"
+#include "userdbd-manager.h"
+
+#define LISTEN_TIMEOUT_USEC (25 * USEC_PER_SEC)
+
+static int start_workers(Manager *m, bool explicit_request);
+
+static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+        Manager *m = userdata;
+
+        assert(s);
+        assert(m);
+
+        for (;;) {
+                siginfo_t siginfo = {};
+                bool removed = false;
+
+                if (waitid(P_ALL, 0, &siginfo, WNOHANG|WEXITED) < 0) {
+                        if (errno == ECHILD)
+                                break;
+
+                        log_warning_errno(errno, "Failed to invoke waitid(): %m");
+                        break;
+                }
+                if (siginfo.si_pid == 0)
+                        break;
+
+                if (set_remove(m->workers_dynamic, PID_TO_PTR(siginfo.si_pid)))
+                        removed = true;
+                if (set_remove(m->workers_fixed, PID_TO_PTR(siginfo.si_pid)))
+                        removed = true;
+
+                if (!removed) {
+                        log_warning("Weird, got SIGCHLD for unknown child " PID_FMT ", ignoring.", siginfo.si_pid);
+                        continue;
+                }
+
+                if (siginfo.si_code == CLD_EXITED) {
+                        if (siginfo.si_status == EXIT_SUCCESS)
+                                log_debug("Worker " PID_FMT " exited successfully.", siginfo.si_pid);
+                        else
+                                log_warning("Worker " PID_FMT " died with a failure exit status %i, ignoring.", siginfo.si_pid, siginfo.si_status);
+                } else if (siginfo.si_code == CLD_KILLED)
+                        log_warning("Worker " PID_FMT " was killed by signal %s, ignoring.", siginfo.si_pid, signal_to_string(siginfo.si_status));
+                else if (siginfo.si_code == CLD_DUMPED)
+                        log_warning("Worker " PID_FMT " dumped core by signal %s, ignoring.", siginfo.si_pid, signal_to_string(siginfo.si_status));
+                else
+                        log_warning("Can't handle SIGCHLD of this type");
+        }
+
+        (void) start_workers(m, false); /* Fill up workers again if we fell below the low watermark */
+        return 0;
+}
+
+static int on_sigusr2(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+        Manager *m = userdata;
+
+        assert(s);
+        assert(m);
+
+        (void) start_workers(m, true); /* Workers told us there's more work, let's add one more worker as long as we are below the high watermark */
+        return 0;
+}
+
+int manager_new(Manager **ret) {
+        _cleanup_(manager_freep) Manager *m = NULL;
+        int r;
+
+        m = new(Manager, 1);
+        if (!m)
+                return -ENOMEM;
+
+        *m = (Manager) {
+                .listen_fd = -1,
+                .worker_ratelimit = {
+                        .interval = 5 * USEC_PER_SEC,
+                        .burst = 50,
+                },
+        };
+
+        r = sd_event_new(&m->event);
+        if (r < 0)
+                return r;
+
+        r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
+        if (r < 0)
+                return r;
+
+        r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
+        if (r < 0)
+                return r;
+
+        (void) sd_event_set_watchdog(m->event, true);
+
+        m->workers_fixed = set_new(NULL);
+        m->workers_dynamic = set_new(NULL);
+
+        if (!m->workers_fixed || !m->workers_dynamic)
+                return -ENOMEM;
+
+        r = sd_event_add_signal(m->event, &m->sigusr2_event_source, SIGUSR2, on_sigusr2, m);
+        if (r < 0)
+                return r;
+
+        r = sd_event_add_signal(m->event, &m->sigchld_event_source, SIGCHLD, on_sigchld, m);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(m);
+        return 0;
+}
+
+Manager* manager_free(Manager *m) {
+        if (!m)
+                return NULL;
+
+        set_free(m->workers_fixed);
+        set_free(m->workers_dynamic);
+
+        sd_event_source_disable_unref(m->sigusr2_event_source);
+        sd_event_source_disable_unref(m->sigchld_event_source);
+
+        sd_event_unref(m->event);
+
+        return mfree(m);
+}
+
+static size_t manager_current_workers(Manager *m) {
+        assert(m);
+
+        return set_size(m->workers_fixed) + set_size(m->workers_dynamic);
+}
+
+static int start_one_worker(Manager *m) {
+        bool fixed;
+        pid_t pid;
+        int r;
+
+        assert(m);
+
+        fixed = set_size(m->workers_fixed) < USERDB_WORKERS_MIN;
+
+        r = safe_fork("(sd-worker)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+        if (r < 0)
+                return log_error_errno(r, "Failed to fork new worker child: %m");
+        if (r == 0) {
+                char pids[DECIMAL_STR_MAX(pid_t)];
+                /* Child */
+
+                log_close();
+
+                r = close_all_fds(&m->listen_fd, 1);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to close fds in child: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                log_open();
+
+                if (m->listen_fd == 3) {
+                        r = fd_cloexec(3, false);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to turn off O_CLOEXEC for fd 3: %m");
+                                _exit(EXIT_FAILURE);
+                        }
+                } else {
+                        if (dup2(m->listen_fd, 3) < 0) { /* dup2() creates with O_CLOEXEC off */
+                                log_error_errno(errno, "Failed to move listen fd to 3: %m");
+                                _exit(EXIT_FAILURE);
+                        }
+
+                        safe_close(m->listen_fd);
+                }
+
+                xsprintf(pids, PID_FMT, pid);
+                if (setenv("LISTEN_PID", pids, 1) < 0) {
+                        log_error_errno(errno, "Failed to set $LISTEN_PID: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                if (setenv("LISTEN_FDS", "1", 1) < 0) {
+                        log_error_errno(errno, "Failed to set $LISTEN_FDS: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+
+                if (setenv("USERDB_FIXED_WORKER", one_zero(fixed), 1) < 0) {
+                        log_error_errno(errno, "Failed to set $USERDB_FIXED_WORKER: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                /* execl("/home/lennart/projects/systemd/build/systemd-userwork", "systemd-userwork", "xxxxxxxxxxxxxxxx", NULL); /\* With some extra space rename_process() can make use of *\/ */
+                /* execl("/usr/bin/valgrind", "valgrind", "/home/lennart/projects/systemd/build/systemd-userwork", "systemd-userwork", "xxxxxxxxxxxxxxxx", NULL); /\* With some extra space rename_process() can make use of *\/ */
+
+                execl(SYSTEMD_USERWORK_PATH, "systemd-userwork", "xxxxxxxxxxxxxxxx", NULL); /* With some extra space rename_process() can make use of */
+                log_error_errno(errno, "Failed start worker process: %m");
+                _exit(EXIT_FAILURE);
+        }
+
+        if (fixed)
+                r = set_put(m->workers_fixed, PID_TO_PTR(pid));
+        else
+                r = set_put(m->workers_dynamic, PID_TO_PTR(pid));
+        if (r < 0)
+                return log_error_errno(r, "Failed to add child process to set: %m");
+
+        return 0;
+}
+
+static int start_workers(Manager *m, bool explicit_request) {
+        int r;
+
+        assert(m);
+
+        for (;;)  {
+                size_t n;
+
+                n = manager_current_workers(m);
+                if (n >= USERDB_WORKERS_MIN && (!explicit_request || n >= USERDB_WORKERS_MAX))
+                        break;
+
+                if (!ratelimit_below(&m->worker_ratelimit)) {
+                        /* If we keep starting workers too often, let's fail the whole daemon, something is wrong */
+                        sd_event_exit(m->event, EXIT_FAILURE);
+
+                        return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN), "Worker threads requested too frequently, something is wrong.");
+                }
+
+                r = start_one_worker(m);
+                if (r < 0)
+                        return r;
+
+                explicit_request = false;
+        }
+
+        return 0;
+}
+
+int manager_startup(Manager *m) {
+        struct timeval ts;
+        int n, r;
+
+        assert(m);
+        assert(m->listen_fd < 0);
+
+        n = sd_listen_fds(false);
+        if (n < 0)
+                return log_error_errno(n, "Failed to determine number of passed file descriptors: %m");
+        if (n > 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected one listening fd, got %i.", n);
+        if (n == 1)
+                m->listen_fd = SD_LISTEN_FDS_START;
+        else {
+                union sockaddr_union sockaddr = {
+                        .un.sun_family = AF_UNIX,
+                        .un.sun_path = "/run/systemd/userdb/io.systemd.NameServiceSwitch",
+                };
+
+                r = mkdir_p("/run/systemd/userdb", 0755);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to create /run/systemd/userdb: %m");
+
+                m->listen_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+                if (m->listen_fd < 0)
+                        return log_error_errno(errno, "Failed to bind on socket: %m");
+
+                (void) sockaddr_un_unlink(&sockaddr.un);
+
+                RUN_WITH_UMASK(0000)
+                        if (bind(m->listen_fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0)
+                                return log_error_errno(errno, "Failed to bind socket: %m");
+
+                r = symlink_idempotent("io.systemd.NameServiceSwitch", "/run/systemd/userdb/io.systemd.Multiplexer", false);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to bind io.systemd.Multiplexer: %m");
+
+                if (listen(m->listen_fd, SOMAXCONN) < 0)
+                        return log_error_errno(errno, "Failed to listen on socket: %m");
+        }
+
+        /* Let's make sure every accept() call on this socket times out after 25s. This allows workers to be
+         * GC'ed on idle */
+        if (setsockopt(m->listen_fd, SOL_SOCKET, SO_RCVTIMEO, timeval_store(&ts, LISTEN_TIMEOUT_USEC), sizeof(ts)) < 0)
+                return log_error_errno(errno, "Failed to se SO_RCVTIMEO: %m");
+
+        return start_workers(m, false);
+}
diff --git a/src/userdb/userdbd-manager.h b/src/userdb/userdbd-manager.h
new file mode 100644 (file)
index 0000000..0bf67fe
--- /dev/null
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+#include "sd-event.h"
+
+typedef struct Manager Manager;
+
+#include "hashmap.h"
+#include "varlink.h"
+#include "ratelimit.h"
+
+#define USERDB_WORKERS_MIN 3
+#define USERDB_WORKERS_MAX 4096
+
+struct Manager {
+        sd_event *event;
+
+        Set *workers_fixed;    /* Workers 0…USERDB_WORKERS_MIN */
+        Set *workers_dynamic;  /* Workers USERD_WORKERS_MIN+1…USERDB_WORKERS_MAX */
+
+        sd_event_source *sigusr2_event_source;
+        sd_event_source *sigchld_event_source;
+
+        int listen_fd;
+
+        RateLimit worker_ratelimit;
+};
+
+int manager_new(Manager **ret);
+Manager* manager_free(Manager *m);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
+
+int manager_startup(Manager *m);
diff --git a/src/userdb/userdbd.c b/src/userdb/userdbd.c
new file mode 100644 (file)
index 0000000..978fd1d
--- /dev/null
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "daemon-util.h"
+#include "userdbd-manager.h"
+#include "log.h"
+#include "main-func.h"
+#include "signal-util.h"
+
+/* This service offers two Varlink services, both implementing io.systemd.UserDatabase:
+ *
+ *         → io.systemd.NameServiceSwitch: this is a compatibility interface for glibc NSS: it response to
+ *           name lookups by checking the classic NSS interfaces and responding that.
+ *
+ *         → io.systemd.Multiplexer: this multiplexes lookup requests to all Varlink services that have a
+ *           socket in /run/systemd/userdb/. It's supposed to simplify clients that don't want to implement
+ *           the full iterative logic on their own.
+ */
+
+static int run(int argc, char *argv[]) {
+        _cleanup_(notify_on_cleanup) const char *notify_stop = NULL;
+        _cleanup_(manager_freep) Manager *m = NULL;
+        int r;
+
+        log_setup_service();
+
+        umask(0022);
+
+        if (argc != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
+
+        if (setenv("SYSTEMD_BYPASS_USERDB", "io.systemd.NameServiceSwitch:io.systemd.Multiplexer", 1) < 0)
+                return log_error_errno(errno, "Failed to se $SYSTEMD_BYPASS_USERDB: %m");
+
+        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, SIGUSR2, -1) >= 0);
+
+        r = manager_new(&m);
+        if (r < 0)
+                return log_error_errno(r, "Could not create manager: %m");
+
+        r = manager_startup(m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to start up daemon: %m");
+
+        notify_stop = notify_start(NOTIFY_READY, NOTIFY_STOPPING);
+
+        r = sd_event_loop(m->event);
+        if (r < 0)
+                return log_error_errno(r, "Event loop failed: %m");
+
+        return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/userdb/userwork.c b/src/userdb/userwork.c
new file mode 100644 (file)
index 0000000..df11b5b
--- /dev/null
@@ -0,0 +1,778 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <poll.h>
+#include <sys/wait.h>
+
+#include "sd-daemon.h"
+
+#include "env-util.h"
+#include "fd-util.h"
+#include "group-record-nss.h"
+#include "group-record.h"
+#include "main-func.h"
+#include "process-util.h"
+#include "strv.h"
+#include "time-util.h"
+#include "user-record-nss.h"
+#include "user-record.h"
+#include "user-util.h"
+#include "userdb.h"
+#include "varlink.h"
+
+#define ITERATIONS_MAX 64U
+#define RUNTIME_MAX_USEC (5 * USEC_PER_MINUTE)
+#define PRESSURE_SLEEP_TIME_USEC (50 * USEC_PER_MSEC)
+#define CONNECTION_IDLE_USEC (15 * USEC_PER_SEC)
+#define LISTEN_IDLE_USEC (90 * USEC_PER_SEC)
+
+typedef struct LookupParameters {
+        const char *user_name;
+        const char *group_name;
+        union {
+                uid_t uid;
+                gid_t gid;
+        };
+        const char *service;
+} LookupParameters;
+
+static int add_nss_service(JsonVariant **v) {
+        _cleanup_(json_variant_unrefp) JsonVariant *status = NULL, *z = NULL;
+        char buf[SD_ID128_STRING_MAX];
+        sd_id128_t mid;
+        int r;
+
+        assert(v);
+
+        /* Patch in service field if it's missing. The assumption here is that this field is unset only for
+         * NSS records */
+
+        if (json_variant_by_key(*v, "service"))
+                return 0;
+
+        r = sd_id128_get_machine(&mid);
+        if (r < 0)
+                return r;
+
+        status = json_variant_ref(json_variant_by_key(*v, "status"));
+        z = json_variant_ref(json_variant_by_key(status, sd_id128_to_string(mid, buf)));
+
+        if (json_variant_by_key(z, "service"))
+                return 0;
+
+        r = json_variant_set_field_string(&z, "service", "io.systemd.NameServiceSwitch");
+        if (r < 0)
+                return r;
+
+        r = json_variant_set_field(&status, buf, z);
+        if (r < 0)
+                return r;
+
+        return json_variant_set_field(v, "status", status);
+}
+
+static int build_user_json(Varlink *link, UserRecord *ur, JsonVariant **ret) {
+        _cleanup_(user_record_unrefp) UserRecord *stripped = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        UserRecordLoadFlags flags;
+        uid_t peer_uid;
+        bool trusted;
+        int r;
+
+        assert(ur);
+        assert(ret);
+
+        r = varlink_get_peer_uid(link, &peer_uid);
+        if (r < 0) {
+                log_debug_errno(r, "Unable to query peer UID, ignoring: %m");
+                trusted = false;
+        } else
+                trusted = peer_uid == 0 || peer_uid == ur->uid;
+
+        flags = USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_BINDING|USER_RECORD_STRIP_SECRET|USER_RECORD_ALLOW_STATUS|USER_RECORD_ALLOW_SIGNATURE;
+        if (trusted)
+                flags |= USER_RECORD_ALLOW_PRIVILEGED;
+        else
+                flags |= USER_RECORD_STRIP_PRIVILEGED;
+
+        r = user_record_clone(ur, flags, &stripped);
+        if (r < 0)
+                return r;
+
+        stripped->incomplete =
+                ur->incomplete ||
+                (FLAGS_SET(ur->mask, USER_RECORD_PRIVILEGED) &&
+                 !FLAGS_SET(stripped->mask, USER_RECORD_PRIVILEGED));
+
+        v = json_variant_ref(stripped->json);
+        r = add_nss_service(&v);
+        if (r < 0)
+                return r;
+
+        return json_build(ret, JSON_BUILD_OBJECT(
+                                          JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(v)),
+                                          JSON_BUILD_PAIR("incomplete", JSON_BUILD_BOOLEAN(stripped->incomplete))));
+}
+
+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), 0 },
+                { "service",  JSON_VARIANT_STRING,   json_dispatch_const_string, offsetof(LookupParameters, service),   0 },
+                {}
+        };
+
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
+        LookupParameters p = {
+                .uid = UID_INVALID,
+        };
+        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.NameServiceSwitch")) {
+                if (uid_is_valid(p.uid))
+                        r = nss_user_record_by_uid(p.uid, &hr);
+                else if (p.user_name)
+                        r = nss_user_record_by_name(p.user_name, &hr);
+                else {
+                        _cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
+
+                        setpwent();
+
+                        for (;;) {
+                                _cleanup_(user_record_unrefp) UserRecord *z = NULL;
+                                _cleanup_free_ char *sbuf = NULL;
+                                struct passwd *pw;
+                                struct spwd spwd;
+
+                                errno = 0;
+                                pw = getpwent();
+                                if (!pw) {
+                                        if (errno != 0)
+                                                log_debug_errno(errno, "Failure while iterating through NSS user database, ignoring: %m");
+
+                                        break;
+                                }
+
+                                r = nss_spwd_for_passwd(pw, &spwd, &sbuf);
+                                if (r < 0)
+                                        log_debug_errno(r, "Failed to acquire shadow entry for user %s, ignoring: %m", pw->pw_name);
+
+                                r = nss_passwd_to_user_record(pw, NULL, &z);
+                                if (r < 0) {
+                                        endpwent();
+                                        return r;
+                                }
+
+                                if (last) {
+                                        r = varlink_notify(link, last);
+                                        if (r < 0) {
+                                                endpwent();
+                                                return r;
+                                        }
+
+                                        last = json_variant_unref(last);
+                                }
+
+                                r = build_user_json(link, z, &last);
+                                if (r < 0) {
+                                        endpwent();
+                                        return r;
+                                }
+                        }
+
+                        endpwent();
+
+                        if (!last)
+                                return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+                        return varlink_reply(link, last);
+                }
+
+        } else if (streq_ptr(p.service, "io.systemd.Multiplexer")) {
+
+                if (uid_is_valid(p.uid))
+                        r = userdb_by_uid(p.uid, USERDB_AVOID_MULTIPLEXER, &hr);
+                else if (p.user_name)
+                        r = userdb_by_name(p.user_name, USERDB_AVOID_MULTIPLEXER, &hr);
+                else {
+                        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+                        _cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
+
+                        r = userdb_all(USERDB_AVOID_MULTIPLEXER, &iterator);
+                        if (r < 0)
+                                return r;
+
+                        for (;;) {
+                                _cleanup_(user_record_unrefp) UserRecord *z = NULL;
+
+                                r = userdb_iterator_get(iterator, &z);
+                                if (r == -ESRCH)
+                                        break;
+                                if (r < 0)
+                                        return r;
+
+                                if (last) {
+                                        r = varlink_notify(link, last);
+                                        if (r < 0)
+                                                return r;
+
+                                        last = json_variant_unref(last);
+                                }
+
+                                r = build_user_json(link, z, &last);
+                                if (r < 0)
+                                        return r;
+                        }
+
+                        if (!last)
+                                return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+                        return varlink_reply(link, last);
+                }
+        } else
+                return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+        if (r == -ESRCH)
+                return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+        if (r < 0) {
+                log_debug_errno(r, "User lookup failed abnormally: %m");
+                return varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
+        }
+
+        if ((uid_is_valid(p.uid) && hr->uid != p.uid) ||
+            (p.user_name && !streq(hr->user_name, p.user_name)))
+                return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
+
+        r = build_user_json(link, hr, &v);
+        if (r < 0)
+                return r;
+
+        return varlink_reply(link, v);
+}
+
+static int build_group_json(Varlink *link, GroupRecord *gr, JsonVariant **ret) {
+        _cleanup_(group_record_unrefp) GroupRecord *stripped = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        UserRecordLoadFlags flags;
+        uid_t peer_uid;
+        bool trusted;
+        int r;
+
+        assert(gr);
+        assert(ret);
+
+        r = varlink_get_peer_uid(link, &peer_uid);
+        if (r < 0) {
+                log_debug_errno(r, "Unable to query peer UID, ignoring: %m");
+                trusted = false;
+        } else
+                trusted = peer_uid == 0;
+
+        flags = USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_BINDING|USER_RECORD_STRIP_SECRET|USER_RECORD_ALLOW_STATUS|USER_RECORD_ALLOW_SIGNATURE;
+        if (trusted)
+                flags |= USER_RECORD_ALLOW_PRIVILEGED;
+        else
+                flags |= USER_RECORD_STRIP_PRIVILEGED;
+
+        r = group_record_clone(gr, flags, &stripped);
+        if (r < 0)
+                return r;
+
+        stripped->incomplete =
+                gr->incomplete ||
+                (FLAGS_SET(gr->mask, USER_RECORD_PRIVILEGED) &&
+                 !FLAGS_SET(stripped->mask, USER_RECORD_PRIVILEGED));
+
+        v = json_variant_ref(gr->json);
+        r = add_nss_service(&v);
+        if (r < 0)
+                return r;
+
+        return json_build(ret, JSON_BUILD_OBJECT(
+                                          JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(v)),
+                                          JSON_BUILD_PAIR("incomplete", JSON_BUILD_BOOLEAN(stripped->incomplete))));
+}
+
+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), 0 },
+                { "service",   JSON_VARIANT_STRING,   json_dispatch_const_string, offsetof(LookupParameters, service),    0 },
+                {}
+        };
+
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
+        LookupParameters p = {
+                .gid = GID_INVALID,
+        };
+        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.NameServiceSwitch")) {
+
+                if (gid_is_valid(p.gid))
+                        r = nss_group_record_by_gid(p.gid, &g);
+                else if (p.group_name)
+                        r = nss_group_record_by_name(p.group_name, &g);
+                else {
+                        _cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
+
+                        setgrent();
+
+                        for (;;) {
+                                _cleanup_(group_record_unrefp) GroupRecord *z = NULL;
+                                _cleanup_free_ char *sbuf = NULL;
+                                struct group *grp;
+                                struct sgrp sgrp;
+
+                                errno = 0;
+                                grp = getgrent();
+                                if (!grp) {
+                                        if (errno != 0)
+                                                log_debug_errno(errno, "Failure while iterating through NSS group database, ignoring: %m");
+
+                                        break;
+                                }
+
+                                r = nss_sgrp_for_group(grp, &sgrp, &sbuf);
+                                if (r < 0)
+                                        log_debug_errno(r, "Failed to acquire shadow entry for group %s, ignoring: %m", grp->gr_name);
+
+                                r = nss_group_to_group_record(grp, r >= 0 ? &sgrp : NULL, &z);
+                                if (r < 0) {
+                                        endgrent();
+                                        return r;
+                                }
+
+                                if (last) {
+                                        r = varlink_notify(link, last);
+                                        if (r < 0) {
+                                                endgrent();
+                                                return r;
+                                        }
+
+                                        last = json_variant_unref(last);
+                                }
+
+                                r = build_group_json(link, z, &last);
+                                if (r < 0) {
+                                        endgrent();
+                                        return r;
+                                }
+                        }
+
+                        endgrent();
+
+                        if (!last)
+                                return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+                        return varlink_reply(link, last);
+                }
+
+        } else if (streq_ptr(p.service, "io.systemd.Multiplexer")) {
+
+                if (gid_is_valid(p.gid))
+                        r = groupdb_by_gid(p.gid, USERDB_AVOID_MULTIPLEXER, &g);
+                else if (p.group_name)
+                        r = groupdb_by_name(p.group_name, USERDB_AVOID_MULTIPLEXER, &g);
+                else {
+                        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+                        _cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
+
+                        r = groupdb_all(USERDB_AVOID_MULTIPLEXER, &iterator);
+                        if (r < 0)
+                                return r;
+
+                        for (;;) {
+                                _cleanup_(group_record_unrefp) GroupRecord *z = NULL;
+
+                                r = groupdb_iterator_get(iterator, &z);
+                                if (r == -ESRCH)
+                                        break;
+                                if (r < 0)
+                                        return r;
+
+                                if (last) {
+                                        r = varlink_notify(link, last);
+                                        if (r < 0)
+                                                return r;
+
+                                        last = json_variant_unref(last);
+                                }
+
+                                r = build_group_json(link, z, &last);
+                                if (r < 0)
+                                        return r;
+                        }
+
+                        if (!last)
+                                return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+                        return varlink_reply(link, last);
+                }
+        } else
+                return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+        if (r == -ESRCH)
+                return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+        if (r < 0) {
+                log_debug_errno(r, "Group lookup failed abnormally: %m");
+                return varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
+        }
+
+        if ((uid_is_valid(p.gid) && g->gid != p.gid) ||
+            (p.group_name && !streq(g->group_name, p.group_name)))
+                return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
+
+        r = build_group_json(link, g, &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), 0 },
+                { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), 0 },
+                { "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.NameServiceSwitch")) {
+
+                if (p.group_name) {
+                        _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
+                        const char *last = NULL;
+                        char **i;
+
+                        r = nss_group_record_by_name(p.group_name, &g);
+                        if (r == -ESRCH)
+                                return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+                        if (r < 0)
+                                return r;
+
+                        STRV_FOREACH(i, g->members) {
+
+                                if (p.user_name && !streq_ptr(p.user_name, *i))
+                                        continue;
+
+                                if (last) {
+                                        r = varlink_notifyb(link, JSON_BUILD_OBJECT(
+                                                                            JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last)),
+                                                                            JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(g->group_name))));
+                                        if (r < 0)
+                                                return r;
+                                }
+
+                                last = *i;
+                        }
+
+                        if (!last)
+                                return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+
+                        return varlink_replyb(link, JSON_BUILD_OBJECT(
+                                                              JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last)),
+                                                              JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(g->group_name))));
+                } else {
+                        _cleanup_free_ char *last_user_name = NULL, *last_group_name = NULL;
+
+                        setgrent();
+
+                        for (;;) {
+                                struct group *grp;
+                                const char* two[2], **users, **i;
+
+                                errno = 0;
+                                grp = getgrent();
+                                if (!grp) {
+                                        if (errno != 0)
+                                                log_debug_errno(errno, "Failure while iterating through NSS group database, ignoring: %m");
+
+                                        break;
+                                }
+
+                                if (p.user_name) {
+                                        if (!strv_contains(grp->gr_mem, p.user_name))
+                                                continue;
+
+                                        two[0] = p.user_name;
+                                        two[1] = NULL;
+
+                                        users = two;
+                                } else
+                                        users = (const char**) grp->gr_mem;
+
+                                STRV_FOREACH(i, users) {
+
+                                        if (last_user_name) {
+                                                assert(last_group_name);
+
+                                                r = varlink_notifyb(link, JSON_BUILD_OBJECT(
+                                                                                    JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
+                                                                                    JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
+                                                if (r < 0) {
+                                                        endgrent();
+                                                        return r;
+                                                }
+
+                                                free(last_user_name);
+                                                free(last_group_name);
+                                        }
+
+                                        last_user_name = strdup(*i);
+                                        last_group_name = strdup(grp->gr_name);
+                                        if (!last_user_name || !last_group_name) {
+                                                endgrent();
+                                                return -ENOMEM;
+                                        }
+                                }
+                        }
+
+                        endgrent();
+
+                        if (!last_user_name) {
+                                assert(!last_group_name);
+                                return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+                        }
+
+                        assert(last_group_name);
+
+                        return varlink_replyb(link, JSON_BUILD_OBJECT(
+                                                              JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
+                                                              JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
+                }
+
+        } else if (streq_ptr(p.service, "io.systemd.Multiplexer")) {
+
+                _cleanup_free_ char *last_user_name = NULL, *last_group_name = NULL;
+                _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+
+                if (p.group_name)
+                        r = membershipdb_by_group(p.group_name, USERDB_AVOID_MULTIPLEXER, &iterator);
+                else if (p.user_name)
+                        r = membershipdb_by_user(p.user_name, USERDB_AVOID_MULTIPLEXER, &iterator);
+                else
+                        r = membershipdb_all(USERDB_AVOID_MULTIPLEXER, &iterator);
+                if (r < 0)
+                        return r;
+
+                for (;;) {
+                        _cleanup_free_ char *user_name = NULL, *group_name = NULL;
+
+                        r = membershipdb_iterator_get(iterator, &user_name, &group_name);
+                        if (r == -ESRCH)
+                                break;
+                        if (r < 0)
+                                return r;
+
+                        /* If both group + user are specified do a-posteriori filtering */
+                        if (p.group_name && p.user_name && !streq(group_name, p.group_name))
+                                continue;
+
+                        if (last_user_name) {
+                                assert(last_group_name);
+
+                                r = varlink_notifyb(link, JSON_BUILD_OBJECT(
+                                                                    JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
+                                                                    JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
+                                if (r < 0)
+                                        return r;
+
+                                free(last_user_name);
+                                free(last_group_name);
+                        }
+
+                        last_user_name = TAKE_PTR(user_name);
+                        last_group_name = TAKE_PTR(group_name);
+                }
+
+                if (!last_user_name) {
+                        assert(!last_group_name);
+                        return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+                }
+
+                assert(last_group_name);
+
+                return varlink_replyb(link, JSON_BUILD_OBJECT(
+                                                      JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
+                                                      JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
+        }
+
+        return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+}
+
+static int process_connection(VarlinkServer *server, int fd) {
+        _cleanup_(varlink_close_unrefp) Varlink *vl = NULL;
+        int r;
+
+        r = varlink_server_add_connection(server, fd, &vl);
+        if (r < 0) {
+                fd = safe_close(fd);
+                return log_error_errno(r, "Failed to add connection: %m");
+        }
+
+        vl = varlink_ref(vl);
+
+        for (;;) {
+                r = varlink_process(vl);
+                if (r == -ENOTCONN) {
+                        log_debug("Connection terminated.");
+                        break;
+                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to process connection: %m");
+                if (r > 0)
+                        continue;
+
+                r = varlink_wait(vl, CONNECTION_IDLE_USEC);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to wait for connection events: %m");
+                if (r == 0)
+                        break;
+        }
+
+        return 0;
+}
+
+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;
+
+        log_setup_service();
+
+        m = sd_listen_fds(false);
+        if (m < 0)
+                return log_error_errno(m, "Failed to determine number of listening fds: %m");
+        if (m == 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No socket to listen on received.");
+        if (m > 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Worker can only listen on a single socket at a time.");
+
+        listen_fd = SD_LISTEN_FDS_START;
+
+        r = fd_nonblock(listen_fd, false);
+        if (r < 0)
+                return log_error_errno(r, "Failed to turn off non-blocking mode for listening socket: %m");
+
+        r = varlink_server_new(&server, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate server: %m");
+
+        r = varlink_server_bind_method_many(
+                        server,
+                        "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 bind methods: %m");
+
+        r = getenv_bool("USERDB_FIXED_WORKER");
+        if (r < 0)
+                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)
+                return log_error_errno(r, "Failed to disable userdb NSS compatibility: %m");
+
+        start_time = now(CLOCK_MONOTONIC);
+
+        for (;;) {
+                _cleanup_close_ int fd = -1;
+                usec_t n;
+
+                /* Exit the worker in regular intervals, to flush out all memory use */
+                if (n_iterations++ > ITERATIONS_MAX) {
+                        log_debug("Exiting worker, processed %u iterations, that's enough.", n_iterations);
+                        break;
+                }
+
+                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));
+                        break;
+                }
+
+                if (last_busy_usec == USEC_INFINITY)
+                        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));
+                        break;
+                }
+
+                (void) rename_process("systemd-userwork: waiting...");
+
+                fd = accept4(listen_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
+                if (fd < 0)
+                        fd = -errno;
+
+                (void) rename_process("systemd-userwork: processing...");
+
+                if (fd == -EAGAIN)
+                        continue; /* The listening socket as SO_RECVTIMEO set, hence a time-out 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
+                                   * case */
+                if (fd < 0)
+                        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");
+
+                        if (FLAGS_SET(pfd.revents, POLLIN)) {
+                                pid_t parent;
+
+                                parent = getppid();
+                                if (parent <= 1)
+                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Parent already died?");
+
+                                if (kill(parent, SIGUSR1) < 0)
+                                        return log_error_errno(errno, "Failed to kill our own parent.");
+                        }
+                }
+
+                (void) process_connection(server, TAKE_FD(fd));
+                last_busy_usec = USEC_INFINITY;
+        }
+
+        return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
index 9f82d905a7b51714cf11c559df54091e98dc1805..4219dce83a971ce45d90edf09bcf1707cdeafbf7 100644 (file)
@@ -1 +1,8 @@
+/* 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
+ *   'udevadm version',
+ * - where a simplified machine-parsable form is more useful, for example
+ *   pkgconfig files and version information written to binary files.
+ */
 #define GIT_VERSION "@VCS_TAG@"
index 41bd1f91832f56c440262dbd94476981e8051031..14378b24af10b4e57bc9094ef7a3dee4d5d397f3 100644 (file)
@@ -22,13 +22,19 @@ kernel.sysrq = 16
 kernel.core_uses_pid = 1
 
 # Source route verification
-net.ipv4.conf.all.rp_filter = 2
+net.ipv4.conf.default.rp_filter = 2
+net.ipv4.conf.*.rp_filter = 2
+-net.ipv4.conf.all.rp_filter
 
 # Do not accept source routing
-net.ipv4.conf.all.accept_source_route = 0
+net.ipv4.conf.default.accept_source_route = 0
+net.ipv4.conf.*.accept_source_route = 0
+-net.ipv4.conf.all.accept_source_route
 
 # Promote secondary addresses when the primary address is removed
-net.ipv4.conf.all.promote_secondaries = 1
+net.ipv4.conf.default.promote_secondaries = 1
+net.ipv4.conf.*.promote_secondaries = 1
+-net.ipv4.conf.all.promote_secondaries
 
 # ping(8) without CAP_NET_ADMIN and CAP_NET_RAW
 # The upper limit is set to 2^31-1. Values greater than that get rejected by
index 007ee7a91f6a99711ae8619bc77ff7b888ced4a4..0eaa8f991ad8bf18f0703814908ce1cb6cde9807 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="Basic systemd setup"
 RUN_IN_UNPRIVILEGED_CONTAINER=${RUN_IN_UNPRIVILEGED_CONTAINER:-yes}
index a7563f446206a54b86455713569739c5d2224494..a859b345d08d10f8c5d453362d1c81192c9d5646 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="cryptsetup systemd setup"
 TEST_NO_NSPAWN=1
index fca6cccb4fb1820944f9f640090fe606b474e00d..85efeeb741c3428736606145841984f1f569ccb6 100755 (executable)
@@ -1,4 +1,5 @@
-#!/bin/bash -ex
+#!/usr/bin/env bash
+set -ex
 
 # Test merging of a --job-mode=ignore-dependencies job into a previously
 # installed job.
index 50b7bce3ed910a35f5b95aea59093d4e9224e0da..5299464b81d46805cde46ef0dd1b5a99f4cbc34f 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="Job-related tests"
 TEST_NO_QEMU=1
index de27eb00645afb6cbb908be1da6e8313da320e09..1431dad862bf86af4d47bfb59920ef4112281e7c 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -x
 set -e
 set -o pipefail
index a18d21533ed05595b92f85c8eeaf8d2dcb9f7e3d..af96dfd7191a7563b319404ec974bdb12b59175d 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="Journal-related tests"
 
index b4d79c9655c4d0d1ca4742cba034e889c485833e..86b57601feae7d34afb0a2edc628311bf1108fb1 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -x
 set -e
 set -o pipefail
index 87fb89e781c11c55981acd65bb6af37d50651cb7..bda37ef212c16e4da458fd88072754341a532aa8 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="Resource limits-related tests"
 
index dae48cd4b2f7076b877e8c72007b72cd39c538bc..9e722e362e3157872dc5d6a793141649cb606b57 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -x
 set -e
 set -o pipefail
index 646dfe024a3be2b58d2fa26d5653b9cedbfabac2..46dc1cd805e36a430bd25554c33124d7664aad3b 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="SELinux tests"
 TEST_NO_NSPAWN=1
index 82339c23cda0fe83104f2a6560dfbff88518ad37..fbb2d1d30a17a813f6ad68f9962bdf25dbc8fa3c 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -x
 set -e
 
index e72c11fd1bca04e7d0c3894c25988a3ec6905ee3..7927294a8ab81c3fe2b238df94137a9f2ab67a2a 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/1981"
 TEST_NO_QEMU=1
index dd879131435134318c990323412f5581b3ebd730..2fedef7b58a83f646f93c786f0c19b371bff39a5 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2730"
 TEST_NO_NSPAWN=1
index 955538c85074bf715134b038546a63ca6996ae7a..efe75d140b683dc71512ed08a4023ca9a094e01e 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2691"
 TEST_NO_NSPAWN=1
index 7bb256963008982e6e4a879d1bf73693f36a359b..1761ad1e4304228f444903df3a50220fa034a506 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2467"
 
index 81c12a2783c1e4be7feebde7db18f47a1925c504..e444414a90fe13e2a7fecb311200b2ee6b193102 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/3166"
 TEST_NO_NSPAWN=1
@@ -41,7 +41,8 @@ EOF
 
 
         cat >$initdir/test-fail-on-restart.sh <<'EOF'
-#!/bin/bash -x
+#!/usr/bin/env bash
+set -x
 
 systemctl start fail-on-restart.service
 active_state=$(systemctl show --property ActiveState fail-on-restart.service)
index d9a0e1a3ddcbdf0eff4bd5fd4d0d3fde16756f96..e30c36ed860eaf4166c5791c6e250e067ea6b817 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/3171"
 TEST_NO_QEMU=1
@@ -29,7 +29,7 @@ Type=oneshot
 EOF
 
         cat >$initdir/test-socket-group.sh <<'EOF'
-#!/bin/bash
+#!/usr/bin/env bash
 set -x
 set -e
 set -o pipefail
index 868dfd852a4ff10e4fc507fe9e4e16c61b16adef..08fb5d4aa6d75aa24a88f799901282c53f6c743b 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 set -e
 set -u
index de504df63214d07bad42f0d1656e4da8a396dae1..974b239d80a842f807fb4fc2c155a3da53ba5d99 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="systemd-nspawn smoke test"
 TEST_NO_NSPAWN=1
@@ -33,7 +33,7 @@ Type=oneshot
 EOF
 
         cat >$initdir/test-nspawn.sh <<'EOF'
-#!/bin/bash
+#!/usr/bin/env bash
 set -x
 set -e
 set -u
index 72d7eacbf6f1dd41bc9a8815ea170b30ccfaf66c..74cabf86aaaa51830e1c6bfffef532913b8c44a6 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="/etc/machine-id testing"
 TEST_NO_NSPAWN=1
@@ -29,7 +29,7 @@ Type=oneshot
 EOF
 
 cat >$initdir/test-machine-id-setup.sh <<'EOF'
-#!/bin/bash
+#!/usr/bin/env bash
 
 set -e
 set -x
index a1d0c618d9544f7a0c567ed889c8406f82e3330c..63bbd3505174f4c7b1c1857bf803d4a4a0905f44 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="Dropin tests"
 TEST_NO_QEMU=1
index e7f643f9ad16d1b51abb5a4334870b27c6752a9a..6f98810b30624d09d2ee8479a81be9fd799e3333 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -v -x
 
 rm -f /test.log
index 1fd2768fd2bbf4d559a6fc49ae38f182f45576b6..40bf046dcde9358ff035975ae21d4f526bfc03fd 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -x
 set -e
 set -o pipefail
index c5f8af6ec6124f9b58e93ff2a07c11c09f3aab93..43d9f1278b3c8658b3f5ce5c4aae08ab9ab4340a 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="EXTEND_TIMEOUT_USEC=usec start/runtime/stop tests"
 SKIP_INITRD=yes
index 1cdbbd2d8a0140738f940527e8faa1fdfa7849b2..e196003e808b67a0db533c87d8dc169875c72bab 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="UDEV SYSTEMD_WANTS property"
 TEST_NO_NSPAWN=1
index ceac3697b5f7fd701b743493800537db45e5a430..989c190ce348a3deb2f2f110ed5e5d83288279e3 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -ex
 set -o pipefail
 
index e9f6ee7afe5fd624df1eb4e1dc430559b92cddef..b6231e6f5aec8f1422b018699302e9644f03bf00 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="FailureAction= operation"
 
index 6ab2638d8ce3be0c6d239d8cf5e3759aa3442fd2..e471cda51b58e46940c25cc6e17757919483c7f1 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -ex
 set -o pipefail
 
index 706f0e9fbb4457d0c613cb5d9175810c114f354b..2fbfef30628580eb36236afcf2abaeebdb068527 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="test cgroup delegation in the unified hierarchy"
 TEST_NO_NSPAWN=1
index a2cb2b875fe08d66c6c71e3ab9c8fb3cc0ef6f3f..57831c267f6d976237913c2c1229a7fdb1c559d7 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -ex
 set -o pipefail
 
index 05fa847a656e12b23145f113ecf37f7957b5bdaf..4cf1b79f9a00fa2c14c4d2bf811cbdca6c1debee 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="test changing main PID"
 
index 904ac0e3b865d1f7f44bff854fbee485b738d579..f8940260702b5ce1a754e88dcb4b1a5206f8840f 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -ex
 set -o pipefail
 
@@ -55,7 +55,7 @@ systemd-notify --uid=1000 MAINPID=$$
 test `systemctl show -p MainPID --value testsuite.service` -eq $$
 
 cat >/tmp/mainpid.sh <<EOF
-#!/bin/bash
+#!/usr/bin/env bash
 
 set -eux
 set -o pipefail
@@ -79,7 +79,7 @@ systemd-run --unit=mainpidsh.service -p StandardOutput=tty -p StandardError=tty
 test `systemctl show -p MainPID --value mainpidsh.service` -eq `cat /run/mainpidsh/pid`
 
 cat >/tmp/mainpid2.sh <<EOF
-#!/bin/bash
+#!/usr/bin/env bash
 
 set -eux
 set -o pipefail
@@ -104,7 +104,7 @@ systemd-run --unit=mainpidsh2.service -p StandardOutput=tty -p StandardError=tty
 test `systemctl show -p MainPID --value mainpidsh2.service` -eq `cat /run/mainpidsh2/pid`
 
 cat >/dev/shm/mainpid3.sh <<EOF
-#!/bin/bash
+#!/usr/bin/env bash
 
 set -eux
 set -o pipefail
diff --git a/test/TEST-21-SYSUSERS/test-13.expected-group b/test/TEST-21-SYSUSERS/test-13.expected-group
new file mode 100644 (file)
index 0000000..c78ea54
--- /dev/null
@@ -0,0 +1,5 @@
+hoge:x:300:
+baz:x:302:
+yyy:x:SYSTEM_GID_MAX:
+foo:x:301:
+ccc:x:305:
diff --git a/test/TEST-21-SYSUSERS/test-13.expected-passwd b/test/TEST-21-SYSUSERS/test-13.expected-passwd
new file mode 100644 (file)
index 0000000..ffc20a8
--- /dev/null
@@ -0,0 +1,5 @@
+foo:x:301:301::/:NOLOGIN
+aaa:x:303:302::/:NOLOGIN
+bbb:x:304:302::/:NOLOGIN
+ccc:x:305:305::/:NOLOGIN
+zzz:x:306:SYSTEM_GID_MAX::/:NOLOGIN
diff --git a/test/TEST-21-SYSUSERS/test-13.input b/test/TEST-21-SYSUSERS/test-13.input
new file mode 100644 (file)
index 0000000..bad2f09
--- /dev/null
@@ -0,0 +1,13 @@
+# Ensure that the semantic for the uid:groupname syntax is correct
+#
+#Type Name ID  GECOS HOMEDIR
+g hoge    300     -            -
+u foo     301     -            -
+
+g baz     302     -            -
+u aaa     303:baz -            -
+u bbb     304:baz -            -
+u ccc     305     -            -
+
+g yyy     -
+u zzz     306:yyy
diff --git a/test/TEST-21-SYSUSERS/test-14.expected-group b/test/TEST-21-SYSUSERS/test-14.expected-group
new file mode 100644 (file)
index 0000000..2e619bc
--- /dev/null
@@ -0,0 +1 @@
+pre:x:987:
diff --git a/test/TEST-21-SYSUSERS/test-14.expected-passwd b/test/TEST-21-SYSUSERS/test-14.expected-passwd
new file mode 100644 (file)
index 0000000..62ed4f5
--- /dev/null
@@ -0,0 +1 @@
+aaa:x:SYSTEM_UID_MAX:987::/:NOLOGIN
diff --git a/test/TEST-21-SYSUSERS/test-14.initial-group b/test/TEST-21-SYSUSERS/test-14.initial-group
new file mode 100644 (file)
index 0000000..2e619bc
--- /dev/null
@@ -0,0 +1 @@
+pre:x:987:
diff --git a/test/TEST-21-SYSUSERS/test-14.input b/test/TEST-21-SYSUSERS/test-14.input
new file mode 100644 (file)
index 0000000..0a11a2e
--- /dev/null
@@ -0,0 +1,4 @@
+# Ensure that a preexisting system group can be used as primary
+#
+#Type Name ID  GECOS HOMEDIR
+u aaa -:pre
index a1a2e62ab1b0c55df2255c712cfacf09ae4a6e6f..c4b221af8a6738f5749c9cf69c76ecc260183643 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="Sysuser-related tests"
 
@@ -23,6 +23,7 @@ preprocess() {
     # get this value from config.h, however the autopkgtest fails with
     # it
     SYSTEM_UID_MAX=$(awk 'BEGIN { uid=999 } /^\s*SYS_UID_MAX\s+/ { uid=$2 } END { print uid }' /etc/login.defs)
+    SYSTEM_GID_MAX=$(awk 'BEGIN { gid=999 } /^\s*SYS_GID_MAX\s+/ { gid=$2 } END { print gid }' /etc/login.defs)
 
     # we can't rely on config.h to get the nologin path, as autopkgtest
     # uses pre-compiled binaries, so extract it from the systemd-sysusers
@@ -30,6 +31,7 @@ preprocess() {
     NOLOGIN=$(strings $(type -p systemd-sysusers) | grep nologin)
 
     sed -e "s/SYSTEM_UID_MAX/${SYSTEM_UID_MAX}/g" \
+        -e "s/SYSTEM_GID_MAX/${SYSTEM_GID_MAX}/g" \
         -e "s#NOLOGIN#${NOLOGIN}#g" "$in"
 }
 
@@ -114,6 +116,7 @@ test_run() {
         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
         if ! diff -u $TESTDIR/tmp/err  ${f%.*}.expected-err; then
             echo "**** Unexpected error output for $f"
diff --git a/test/TEST-21-SYSUSERS/unhappy-3.expected-err b/test/TEST-21-SYSUSERS/unhappy-3.expected-err
new file mode 100644 (file)
index 0000000..d55b366
--- /dev/null
@@ -0,0 +1 @@
+Group g1 not found.
diff --git a/test/TEST-21-SYSUSERS/unhappy-3.input b/test/TEST-21-SYSUSERS/unhappy-3.input
new file mode 100644 (file)
index 0000000..64e60dd
--- /dev/null
@@ -0,0 +1,4 @@
+# Ensure it is not allowed to create groups implicitly in the uid:groupname syntax
+#
+#Type Name ID  GECOS HOMEDIR
+u u1 100:g1 -
index 3ad652f4ede0af1fb872bd6ba13850738d70438d..a0158f0421634257d8e7db4f9e47b10c2b4181f2 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 set -x
 set -e
index b69abda6441693febf1e74863ba50d9a5a64b450..c558dfd4db370855b5fd5d3c66e31bad16f86421 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 set -e
 set -x
index 32c3abdeab335f83804c662bf4e51d5e1586638c..aa6efcfb48fc206dbd6788a1c4688a8fca5a6338 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="Tmpfiles related tests"
 TEST_NO_QEMU=1
index 127d39319a5ab0c28eb612eadbccddeb4eb55bfc..ebc9fe4c856d6a82cfcecc676de3aad4d1c72e8d 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="test Type=exec"
 
index 50d6754b96b2550b4ae82fba963a25749560f45f..5e2966f848e0cd60013146a4400023455794cc1c 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -ex
 set -o pipefail
 
index 3c993e67f714db782b5eed31f5157cb073515e9b..c9198fb6c7157bf8beeeda255e591585342b1881 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="Run unit tests under containers"
 RUN_IN_UNPRIVILEGED_CONTAINER=yes
index e33d04d043b27cc2b74686957b0a420c37c96d3f..cc78adbbe94226ee3fa5d9d1b674b9b9154ab714 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 #set -ex
 #set -o pipefail
 
index c0f51f3b897da14c933bde847dd92e748aeb537b..14265cdfc5d246766895873891d8f867ae6c40ca 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="test importd"
 
index d4efd71e0651fe09fec16d6617419d25750bd272..6dcb78050858fc0a0d6d5251edad023e735c3888 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -ex
 set -o pipefail
 
index 065a8e461c8e0211231a215563ce59babe1e8699..ee0f562277b05c2a12cf9121fa6aae7f09b623b4 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="test setenv"
 
index 0abbf95326553a00d2f593d2f4e95b03c3230322..89c0937c8dafc7dbba060a787c3db1749ab9b982 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -ex
 set -o pipefail
 
index c8338736bb05f402e607580b79d5a393c2d74bfd..66c98e5675a01fa82de921e8bde84b4ebf577f32 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="test StandardOutput=file:"
 
index 2c813da720e3afdeab9420689980be7638d9a4b0..c522f75dbce73ebf266b93d92125b57e89dfe5ec 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -ex
 set -o pipefail
 
index 3427ba7a91de2306308d53311b8c626159ddad99..934e1bc70c3965fe3270279f1a919ccc5bbdfad7 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="Ensure %j Wants directives work"
 RUN_IN_UNPRIVILEGED_CONTAINER=yes
index 7d173c8422ae8f7f9b9db080d24fdf1b2ca19ea9..fb570b034701fecc5a2e1e1d34e04ea3846faddb 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="UDEV ID_RENAMING property"
 TEST_NO_NSPAWN=1
index eb9b2ffb89ee51aa55651979d1ec0fd9f64173c9..5abdb53eb31cdba5f8b3a7e8bc0ee53067d335fd 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -ex
 set -o pipefail
 
index 8976e9530835badadb06cdee75d759200b28f56c..9e2c11238c7a505556197988654e57f67f3db6de 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="test OnClockChange= + OnTimezoneChange="
 TEST_NO_NSPAWN=1
index 7b24922e62843fed09aaa7ee988fff13b490911e..a507ffcd7bcee21ecc33617ebb0faeba39ac1c9f 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -ex
 set -o pipefail
 
index e88e0c367a656314632194fab06e45146b51d33c..7bc9298dc12c6720853889356c574549869a9d51 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="plugged -> dead -> plugged issue #11997"
 TEST_NO_NSPAWN=1
index 047c1be06f3d18eeeb54fc6fd49159c8d7cbe044..fcff82d804804673f5fb4b5013c7d14e019e4b30 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 set -o pipefail
 
index 1726b21fc3bf0dc9c58ddd3a43315d327d1367d6..36aad419eac784f13e618c971b82ec1346079f42 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="test OOM killer logic"
 TEST_NO_NSPAWN=1
index 8aab487c6e7fb4708a45a5cf752fddcc99658bd5..aafafc11836d3289f6361faf08dd408f20805ffd 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -ex
 set -o pipefail
 
index b3316bdc2673ed5da2721ff366903164873c4b00..310cf7b1457b890d220e2812c5ae2a40602697b2 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/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
index 0272a2305b592c34dab808415280428543c4a001..0a6ee57b99867cfa7f62c7a8c20d824073cf145e 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/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
index c7c1b29d8613e3b3469352268b8d6b713a0345ac..ad299df42053c559b76331f9e001a9a15f7a0425 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="test migrating state directory from DynamicUser=1 to DynamicUser=0 and back"
 
index 8f5316e3e547c03695f06ac047f277030801b100..6d9488688a1bb4554923154641e6e98e65a714fa 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -ex
 set -o pipefail
 
index 4ee5da533f07374724cabc6cb06b54ee275c24e5..78532818505e8b39b712502177796e90d0c52a77 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="network-generator tests"
 
index e4c0e45ebe0d6d593458ee22af23859d2c92e763..29addc958b3eb5a337a23761f7c9df9ab5cd1933 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 
 TEST_DESCRIPTION="test MUMAPolicy= and NUMAMask= options"
index a5ac788178ec84672a7fd58472f083724b95f6d6..4a2bede431a6fe1459b1a74dcfe641065665ae36 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -ex
 set -o pipefail
 
index ffe0ee9499f7fdbb47001ee208b77770a6bda2e2..2c5ad430b5e8f6da67358b5703b2119b96010bae 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/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
index 4e63a07326ea3ace24022c94c47151126b67211e..32a9dd8694e03c7bfe5f5a16eacba6f38a439980 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/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
index d2b1663aa3c47e61705db1c5819bf8969cf5ea80..ac1d0c9cf6d173635fab7e19fbb418f084821d94 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="Test ExecReload= (PR #13098)"
 
index a51f6fd5cc28a58fba39c69d5d068830a4ace02b..eb7363fa6a2bb182998dc9b92b60763e3069eb6c 100644 (file)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 set -ex
 set -o pipefail
index 01d725eb5e9bd5da7e43f288934414aeef705d90..a7933b007095c12aa354e5f7e7a006d62035e3ef 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="test ExecXYZEx= service unit dbus hookups"
 
index e0580ae75a3a7f4aac8c0a6ab7e79f1bc9acb76f..957d22031a9416a94e293901ee375845bc9482e9 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -ex
 set -o pipefail
 
index 96c21da642161b719af21ff9415cdec063a6db38..54292c0ca22eeabaa2ff0e4abbed9198185c7d73 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="Test oneshot unit restart on failure"
 . $TEST_BASE_DIR/test-functions
index 4465614ff3f5adddd67b38d5f443d1b59afa4ace..905f32e994b25192789534c3729097436077c27e 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -ex
 set -o pipefail
 
index 865989d001bed4dbc331fd526eaf7edd90fdc034..0c393597c79838d8645487b274cce213abb518fc 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="test that ExecStopPost= is always run"
 
index a027890047e651cdd0ca8e172b5eff24b8ee4411..154398dd662e8a68c4c0b7823eb47403d5229515 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -ex
 
 systemd-analyze log-level debug
@@ -16,7 +16,7 @@ test -f /run/exec1
 test -f /run/exec2
 
 cat > /tmp/forking1.sh <<EOF
-#!/bin/bash
+#!/usr/bin/env bash
 
 set -eux
 
@@ -32,7 +32,7 @@ systemd-run --unit=forking1.service --wait -p StandardOutput=tty -p StandardErro
 test -f /run/forking1
 
 cat > /tmp/forking2.sh <<EOF
-#!/bin/bash
+#!/usr/bin/env bash
 
 set -eux
 
@@ -62,7 +62,7 @@ test -f /run/dbus1
 test -f /run/dbus2
 
 cat > /tmp/notify1.sh <<EOF
-#!/bin/bash
+#!/usr/bin/env bash
 
 set -eux
 
index fe20114756b4d9f5af959b2493ee9e50318c07d0..3b40bf7d8bb5b4445fb33d311e6a22199ec9cebc 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="Test PrivateUsers=yes on user manager"
 . $TEST_BASE_DIR/test-functions
index fa5da2ec7d7536246524c62c53b9432d9ffd01c1..ff94ad4d81896a2900ef244985269a911d6718b2 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -ex
 set -o pipefail
 
diff --git a/test/TEST-44-LOG-NAMESPACE/Makefile b/test/TEST-44-LOG-NAMESPACE/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-44-LOG-NAMESPACE/test.sh b/test/TEST-44-LOG-NAMESPACE/test.sh
new file mode 100755 (executable)
index 0000000..c2070f2
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+set -e
+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 "$@"
diff --git a/test/TEST-44-LOG-NAMESPACE/testsuite.sh b/test/TEST-44-LOG-NAMESPACE/testsuite.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/TEST-45-REPART/Makefile b/test/TEST-45-REPART/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-45-REPART/test.sh b/test/TEST-45-REPART/test.sh
new file mode 100755 (executable)
index 0000000..e7015d5
--- /dev/null
@@ -0,0 +1,37 @@
+#!/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
new file mode 100755 (executable)
index 0000000..804faef
--- /dev/null
@@ -0,0 +1,120 @@
+#!/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
diff --git a/test/TEST-46-HOMED/Makefile b/test/TEST-46-HOMED/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-46-HOMED/test.sh b/test/TEST-46-HOMED/test.sh
new file mode 100755 (executable)
index 0000000..99fd5b8
--- /dev/null
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+set -e
+TEST_DESCRIPTION="testing homed"
+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 "$@"
diff --git a/test/TEST-46-HOMED/testsuite.sh b/test/TEST-46-HOMED/testsuite.sh
new file mode 100755 (executable)
index 0000000..9ef9f30
--- /dev/null
@@ -0,0 +1,74 @@
+#!/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/Makefile b/test/TEST-47-ISSUE-14566/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-47-ISSUE-14566/repro.sh b/test/TEST-47-ISSUE-14566/repro.sh
new file mode 100755 (executable)
index 0000000..5217602
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+sleep infinity &
+echo $! > /leakedtestpid
+wait $!
diff --git a/test/TEST-47-ISSUE-14566/test.sh b/test/TEST-47-ISSUE-14566/test.sh
new file mode 100755 (executable)
index 0000000..35f8623
--- /dev/null
@@ -0,0 +1,43 @@
+#!/bin/bash
+set -e
+TEST_DESCRIPTION="Test that KillMode=mixed does not leave left over proccesses 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 "$@"
diff --git a/test/TEST-47-ISSUE-14566/testsuite.sh b/test/TEST-47-ISSUE-14566/testsuite.sh
new file mode 100755 (executable)
index 0000000..d917cf5
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/bash
+set -ex
+set -o pipefail
+
+systemd-analyze log-level debug
+systemd-analyze log-target console
+
+systemctl start issue_14566_test
+systemctl status issue_14566_test
+
+leaked_pid=$(cat /leakedtestpid)
+
+systemctl stop issue_14566_test
+
+# 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
index ba8760f12bab808b87530ab8b4bfebaacc1d81ff..fe71d26d89e866e4dee775b5f1ee83f78be65b8f 100644 (file)
@@ -26,6 +26,8 @@ Duplex=
 AutoNegotiation=
 WakeOnLan=
 Port=
+ReceiveChecksumOffload=
+TransmitChecksumOffload=
 GenericSegmentationOffload=
 TCPSegmentationOffload=
 TCP6SegmentationOffload=
@@ -39,3 +41,6 @@ CombinedChannels=
 Advertise=
 RxBufferSize=
 TxBufferSize=
+RxFlowControl=
+TxFlowControl=
+AutoNegotiationFlowControl=
index 0e3adac5ce1b6c45f61c7d502978d0ca5fc4c9e5..b97daca959e6d761e2d8f9291e1b703125610f5f 100644 (file)
@@ -107,6 +107,7 @@ UseDNS=
 RapidCommit=
 ForceDHCPv6PDOtherInformation=
 PrefixDelegationHint=
+WithoutRA=
 [Route]
 Destination=
 Protocol=
@@ -186,6 +187,7 @@ OnLink=
 PreferredLifetimeSec=
 AddressAutoconfiguration=
 ValidLifetimeSec=
+Assign=
 [IPv6RoutePrefix]
 Route=
 LifetimeSec=
@@ -198,6 +200,7 @@ SamplePoint=
 BitRate=
 RestartSec=
 TripleSampling=
+Termination=
 [Address]
 DuplicateAddressDetection=
 AutoJoin=
@@ -225,6 +228,8 @@ DestinationPort=
 IPProtocol=
 InvertRule=
 Family=
+SuppressPrefixLength=
+User=
 [IPv6PrefixDelegation]
 RouterPreference=
 DNSLifetimeSec=
@@ -247,6 +252,7 @@ Prefix=
 UseDomains=
 RouteTable=
 UseDNS=
+DHCPv6Client=
 UseAutonomousPrefix=
 UseOnLinkPrefix=
 BlackList=
@@ -268,6 +274,67 @@ SendOption=
 [NextHop]
 Id=
 Gateway=
+[QDisc]
+Parent=
+Handle=
+[NetworkEmulator]
+Parent=
+Handle=
+DelaySec=
+DelayJitterSec=
+LossRate=
+DuplicateRate=
+PacketLimit=
+[TokenBucketFilter]
+Parent=
+Handle=
+Rate=
+Burst=
+LimitSize=
+MTUBytes=
+MPUBytes=
+PeakRate=
+LatencySec=
+[StochasticFairnessQueueing]
+Parent=
+Handle=
+PerturbPeriodSec=
+[FairQueueingControlledDelay]
+Parent=
+Handle=
+PacketLimit=
+MemoryLimit=
+Flows=
+Quantum=
+TargetSec=
+IntervalSec=
+CEThresholdSec=
+ECN=
+[FairQueueing]
+Parent=
+Handle=
+PacketLimit=
+FlowLimit=
+Quantum=
+InitialQuantum=
+MaximumRate=
+Buckets=
+OrphanMask=
+Pacing=
+CEThresholdSec=
+[ControlledDelay]
+Parent=
+Handle=
+PacketLimit=
+TargetSec=
+IntervalSec=
+CEThresholdSec=
+ECN=
+[CAKE]
+Parent=
+Handle=
+Bandwidth=
+Overhead=
 [TrafficControlQueueingDiscipline]
 Parent=
 NetworkEmulatorDelaySec=
@@ -275,33 +342,31 @@ NetworkEmulatorDelayJitterSec=
 NetworkEmulatorLossRate=
 NetworkEmulatorDuplicateRate=
 NetworkEmulatorPacketLimit=
-TokenBufferFilterRate=
-TokenBufferFilterBurst=
-TokenBufferFilterLimitSize=
-TokenBufferFilterMTUBytes=
-TokenBufferFilterMPUBytes=
-TokenBufferFilterPeakRate=
-TokenBufferFilterLatencySec=
-StochasticFairnessQueueingPerturbPeriodSec=
-FairQueuingControlledDelayPacketLimit=
-FairQueuingControlledDelayMemoryLimit=
-FairQueuingControlledDelayFlows=
-FairQueuingControlledDelayQuantum=
-FairQueuingControlledDelayTargetSec=
-FairQueuingControlledDelayIntervalSec=
-FairQueuingControlledDelayCEThresholdSec=
-FairQueuingControlledDelayECN=
-FairQueueTrafficPolicingPacketLimit=
-FairQueueTrafficPolicingFlowLimit=
-FairQueueTrafficPolicingQuantum=
-FairQueueTrafficPolicingInitialQuantum=
-FairQueueTrafficPolicingMaximumRate=
-FairQueueTrafficPolicingBuckets=
-FairQueueTrafficPolicingOrphanMask=
-FairQueueTrafficPolicingPacing=
-FairQueueTrafficPolicingCEThresholdSec=
-ControlledDelayPacketLimit=
-ControlledDelayTargetSec=
-ControlledDelayIntervalSec=
-ControlledDelayCEThresholdSec=
-ControlledDelayECN=
+[TrivialLinkEqualizer]
+Parent=
+Handle=
+Id=
+[HierarchyTokenBucket]
+Parent=
+Handle=
+DefaultClass=
+[HierarchyTokenBucketClass]
+Parent=
+ClassId=
+Priority=
+Rate=
+CeilRate=
+[PFIFO]
+Parent=
+Handle=
+PacketLimit=
+[GenericRandomEarlyDetection]
+Parent=
+Handle=
+VirtualQueues=
+DefaultVirtualQueue=
+GenericRIO=
+[StochasticFairBlue]
+Parent=
+Handle=
+PacketLimit=
diff --git a/test/fuzz/fuzz-network-parser/oss-fuzz-20548 b/test/fuzz/fuzz-network-parser/oss-fuzz-20548
new file mode 100644 (file)
index 0000000..82efed5
Binary files /dev/null and b/test/fuzz/fuzz-network-parser/oss-fuzz-20548 differ
index 31a45043daca31d1be54b84a9f1bb43a1f876610..45f8751971b9b199ced97758b187936a4284b5b7 100644 (file)
@@ -538,6 +538,7 @@ STP=
 Scope=
 SendHostname=
 Source=
+SuppressPrefixLength=
 TCP6SegmentationOffload=
 TCPSegmentationOffload=
 TOS=
index 237b4db12c9771271dc720ed34a276e812734574..2fbea31ccd9c76e406bebe772c6c713b04ab112a 100644 (file)
@@ -109,6 +109,7 @@ test_data_files = '''
         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
@@ -138,6 +139,7 @@ test_data_files = '''
         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
index 58af352f6f247e38dea1ae03aab8d18060d66fa0..6032c33c3aec00befeca5f224b8df5e6bc84bb76 100644 (file)
@@ -10,7 +10,6 @@ Release=29
 [Output]
 Format=raw_btrfs
 Bootable=yes
-KernelCommandLine=printk.devkmsg=on
 OutputDirectory=../mkosi.output
 Output=networkd-test.raw
 
index 77b50d7234a761ffdfc7ffbecc7d06925ce80e4f..241583bb4ef73dee989719df8b11e4493e83786c 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 fd=0
 
 OPTIND=1
index 74fd64d24a7183b8c13c020eb2158f7a56509814..c0a8448a88a6f95175075ad27b9b1a45741b0968 100755 (executable)
@@ -1,4 +1,5 @@
-#!/bin/bash -e
+#!/usr/bin/env bash
+set -e
 
 BUILD_DIR="$($(dirname "$0")/../tools/find-build-dir.sh)"
 if [ $# -gt 0 ]; then
index 592b20afcf0886b3a05bd55a056f884a7b4bff12..783b7874500dda96593fa3a8bb29f3e179b1b63c 100755 (executable)
@@ -1,4 +1,5 @@
-#!/bin/bash -e
+#!/usr/bin/env bash
+set -e
 
 out="$1"
 systemd_efi="$2"
index a11c5ecbf02de273596f523d957ce2d0673fe080..8aa16fb7f94838201d1e82bb6d9a31e046965409 100755 (executable)
@@ -11,6 +11,7 @@ import time
 import os
 import tempfile
 import subprocess
+import sys
 
 from enum import Enum
 
@@ -32,7 +33,7 @@ class ExecutionResumeTest(unittest.TestCase):
         unit_file_content = '''
         [Service]
         Type=oneshot
-        ExecStart=/bin/sleep 2
+        ExecStart=/bin/sleep 3
         ExecStart=/bin/bash -c "echo foo >> {0}"
         '''.format(self.output_file)
         self.unit_files[UnitFileChange.NO_CHANGE] = unit_file_content
@@ -41,7 +42,7 @@ class ExecutionResumeTest(unittest.TestCase):
         [Service]
         Type=oneshot
         ExecStart=/bin/bash -c "echo foo >> {0}"
-        ExecStart=/bin/sleep 2
+        ExecStart=/bin/sleep 3
         '''.format(self.output_file)
         self.unit_files[UnitFileChange.LINES_SWAPPED] = unit_file_content
 
@@ -49,7 +50,7 @@ class ExecutionResumeTest(unittest.TestCase):
         [Service]
         Type=oneshot
         ExecStart=/bin/bash -c "echo bar >> {0}"
-        ExecStart=/bin/sleep 2
+        ExecStart=/bin/sleep 3
         ExecStart=/bin/bash -c "echo foo >> {0}"
         '''.format(self.output_file)
         self.unit_files[UnitFileChange.COMMAND_ADDED_BEFORE] = unit_file_content
@@ -57,7 +58,7 @@ class ExecutionResumeTest(unittest.TestCase):
         unit_file_content = '''
         [Service]
         Type=oneshot
-        ExecStart=/bin/sleep 2
+        ExecStart=/bin/sleep 3
         ExecStart=/bin/bash -c "echo foo >> {0}"
         ExecStart=/bin/bash -c "echo bar >> {0}"
         '''.format(self.output_file)
@@ -67,7 +68,7 @@ class ExecutionResumeTest(unittest.TestCase):
         [Service]
         Type=oneshot
         ExecStart=/bin/bash -c "echo baz >> {0}"
-        ExecStart=/bin/sleep 2
+        ExecStart=/bin/sleep 3
         ExecStart=/bin/bash -c "echo foo >> {0}"
         ExecStart=/bin/bash -c "echo bar >> {0}"
         '''.format(self.output_file)
@@ -107,6 +108,7 @@ class ExecutionResumeTest(unittest.TestCase):
     def setup_unit(self):
         self.write_unit_file(UnitFileChange.NO_CHANGE)
         subprocess.check_call(['systemctl', '--job-mode=replace', '--no-block', 'start', self.unit])
+        time.sleep(1)
 
     def test_no_change(self):
         expected_output = 'foo\n'
@@ -207,4 +209,4 @@ class ExecutionResumeTest(unittest.TestCase):
         self.reload()
 
 if __name__ == '__main__':
-    unittest.main()
+    unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=3))
index ae4618c3f375f14c71a94185b074eeba3e4e5be1..60c5be6dc98c2ddce384b04d9db05597c76cb295 100644 (file)
@@ -10,7 +10,6 @@ ExecStart=touch /tmp/a ; /bin/sh -c 'touch /tmp/b' ; touch /tmp/c
 ExecStart=test -f /tmp/a
 ExecStart=!test -f /tmp/b
 ExecStart=!!test -f /tmp/c
-ExecStart=+test -f /tmp/c
 ExecStartPost=rm /tmp/a /tmp/b /tmp/c
 
 PrivateTmp=true
index 1abe3906016d210231d8f4c539e007d36ffb7de3..4486f6c25d87725a0519c4de5d26b42b5e5d3f86 100644 (file)
@@ -2,6 +2,7 @@
 Description=Test for CapabilityBoundingSet
 
 [Service]
-ExecStart=/bin/sh -x -c '! capsh --print | grep "^Bounding set .*cap_chown"'
+# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
+ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep "^Bounding set .*cap_chown"'
 Type=oneshot
 CapabilityBoundingSet=~CAP_CHOWN
index 6d39469da8f26eeaab3e8ba27d4274e399d8a37e..8f135be0b5e902e25da247d108ea96a532fcef22 100644 (file)
@@ -3,5 +3,6 @@ Description=Test CAP_MKNOD capability for PrivateDevices=no
 
 [Service]
 PrivateDevices=no
-ExecStart=/bin/sh -x -c 'capsh --print | grep cap_mknod'
+# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
+ExecStart=/bin/sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_mknod'
 Type=oneshot
index e7f529c44c607f8f31b8fac11fe5e81f67c0c768..30ce5492546f0035f0af154e430eed8bec73ad0c 100644 (file)
@@ -3,5 +3,6 @@ Description=Test CAP_SYS_RAWIO capability for PrivateDevices=no
 
 [Service]
 PrivateDevices=no
-ExecStart=/bin/sh -x -c 'capsh --print | grep cap_sys_rawio'
+# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
+ExecStart=/bin/sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_rawio'
 Type=oneshot
index fb1fc2875a329fda02391b5dbc9bd22292deaea7..b98cfb5c7eaca13f6f059c77e6559f100763073c 100644 (file)
@@ -3,5 +3,6 @@ Description=Test CAP_MKNOD capability for PrivateDevices=yes
 
 [Service]
 PrivateDevices=yes
-ExecStart=/bin/sh -x -c '! capsh --print | grep cap_mknod'
+# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
+ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_mknod'
 Type=oneshot
index cebc493a7abfd4449eb5ba3932f1214fb6469803..5b0c0700f2c89d27b6bfb06f0657d548ae2e255c 100644 (file)
@@ -3,5 +3,6 @@ Description=Test CAP_SYS_RAWIO capability for PrivateDevices=yes
 
 [Service]
 PrivateDevices=yes
-ExecStart=/bin/sh -x -c '! capsh --print | grep cap_sys_rawio'
+# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
+ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_rawio'
 Type=oneshot
diff --git a/test/test-execute/exec-privatetmp-disabled-by-prefix.service b/test/test-execute/exec-privatetmp-disabled-by-prefix.service
new file mode 100644 (file)
index 0000000..009e6be
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=Test for PrivateTmp=yes with prefix
+
+[Service]
+ExecStart=/bin/sh -x -c 'test ! -f /tmp/test-exec_privatetmp'
+ExecStart=+/bin/sh -x -c 'test -f /tmp/test-exec_privatetmp'
+Type=oneshot
+PrivateTmp=yes
index f0c7d4d6858fb441783fd15507a56d0faf2d5afe..36aae7caf1a4553c023d74254e015c8e278be21d 100644 (file)
@@ -3,5 +3,6 @@ Description=Test CAP_SYSLOG for ProtectKernelLogs=no
 
 [Service]
 ProtectKernelLogs=no
-ExecStart=/bin/sh -x -c 'capsh --print | grep cap_syslog'
+# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
+ExecStart=/bin/sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_syslog'
 Type=oneshot
index 803ba7d5521b6ec9c0e0e36588ff866245dc33ec..4a5f1a08e902480435800edcdbb4cfb932f7489a 100644 (file)
@@ -3,5 +3,6 @@ Description=Test CAP_SYSLOG for ProtectKernelLogs=yes
 
 [Service]
 ProtectKernelLogs=yes
-ExecStart=/bin/sh -x -c '! capsh --print | grep cap_syslog'
+# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
+ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_syslog'
 Type=oneshot
index b2f2cd6b8ab0fcf23ec0ba87972104c7759c950d..1b73656305e4455f703fafcb265060f5ad2a9ab2 100644 (file)
@@ -3,5 +3,6 @@ Description=Test CAP_SYS_MODULE ProtectKernelModules=no
 
 [Service]
 ProtectKernelModules=no
-ExecStart=/bin/sh -x -c 'capsh --print | grep cap_sys_module'
+# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
+ExecStart=/bin/sh -x -c 'capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_module'
 Type=oneshot
index 84bf39be56928b0fba273962a5a03518018c1366..e43e72733c93c812d9a11b3493ba5a9a0902750e 100644 (file)
@@ -3,5 +3,6 @@ Description=Test CAP_SYS_MODULE for ProtectKernelModules=yes
 
 [Service]
 ProtectKernelModules=yes
-ExecStart=/bin/sh -x -c '! capsh --print | grep cap_sys_module'
+# sed: remove dropped (cap_xxx-[epi]) and IAB capabilities from the output
+ExecStart=/bin/sh -x -c '! capsh --print | sed -re "s/[^ ]+?\-[epi]+//g" -e '/IAB/d' | grep cap_sys_module'
 Type=oneshot
diff --git a/test/test-execute/exec-standardinput-file-cat.service b/test/test-execute/exec-standardinput-file-cat.service
new file mode 100644 (file)
index 0000000..a0c786c
--- /dev/null
@@ -0,0 +1,9 @@
+[Unit]
+Description=Test for StandardInput=file:
+
+[Service]
+ExecStart=cat
+Type=oneshot
+StandardInput=file:/etc/os-release
+# We leave StandardOutput= unset here, to verify https://github.com/systemd/systemd/issues/14560 works
+# The "cat" tool is going to write to stdout, which fails if we dup() stdin to stdout
index 5fffa5b9f862a20560b692f2d586776755678ae6..66cd60b5592eede503b8826668914b0b16c5b72f 100644 (file)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/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
 PATH=/sbin:/bin:/usr/sbin:/usr/bin
@@ -39,7 +39,7 @@ PATH_TO_INIT=$ROOTLIBDIR/systemd
 [ "$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 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"
+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"
 
 STATEDIR="${BUILD_DIR:-.}/test/$(basename $(dirname $(realpath $0)))"
@@ -212,7 +212,6 @@ loglevel=2 \
 init=$PATH_TO_INIT \
 console=$CONSOLE \
 selinux=0 \
-printk.devkmsg=on \
 $_cgroup_args \
 $KERNEL_APPEND \
 "
@@ -377,7 +376,7 @@ create_valgrind_wrapper() {
     local _valgrind_wrapper=$initdir/$ROOTLIBDIR/systemd-under-valgrind
     ddebug "Create $_valgrind_wrapper"
     cat >$_valgrind_wrapper <<EOF
-#!/bin/bash
+#!/usr/bin/env bash
 
 mount -t proc proc /proc
 exec valgrind --leak-check=full --log-file=/valgrind.out $ROOTLIBDIR/systemd "\$@"
@@ -406,7 +405,7 @@ create_asan_wrapper() {
     esac
 
     cat >$_asan_wrapper <<EOF
-#!/bin/bash
+#!/usr/bin/env bash
 
 set -x
 
@@ -469,9 +468,14 @@ printf "[Service]\nTimeoutSec=180s\n" >/etc/systemd/system/systemd-journal-flush
 # 1
 # Let's workaround this by clearing the previously set LD_PRELOAD env variable,
 # so the libasan library is not loaded for this particular service
-REMOUNTFS_CONF_DIR=/etc/systemd/system/systemd-remount-fs.service.d
-mkdir -p "\$REMOUNTFS_CONF_DIR"
-printf "[Service]\nUnsetEnvironment=LD_PRELOAD\n" >"\$REMOUNTFS_CONF_DIR/env.conf"
+unset_ld_preload() {
+    local _dropin_dir="/etc/systemd/system/\$1.service.d"
+    mkdir -p "\$_dropin_dir"
+    printf "[Service]\nUnsetEnvironment=LD_PRELOAD\n" >"\$_dropin_dir/unset_ld_preload.conf"
+}
+
+unset_ld_preload systemd-remount-fs
+unset_ld_preload testsuite
 
 export ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd.asan.log UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS
 exec  $ROOTLIBDIR/systemd "\$@"
@@ -484,7 +488,7 @@ create_strace_wrapper() {
     local _strace_wrapper=$initdir/$ROOTLIBDIR/systemd-under-strace
     ddebug "Create $_strace_wrapper"
     cat >$_strace_wrapper <<EOF
-#!/bin/bash
+#!/usr/bin/env bash
 
 exec strace -D -o /strace.out $ROOTLIBDIR/systemd "\$@"
 EOF
@@ -671,7 +675,7 @@ strip_binaries() {
 create_rc_local() {
     mkdir -p $initdir/etc/rc.d
     cat >$initdir/etc/rc.d/rc.local <<EOF
-#!/bin/bash
+#!/usr/bin/env bash
 exit 0
 EOF
     chmod 0755 $initdir/etc/rc.d/rc.local
@@ -732,7 +736,6 @@ install_config_files() {
     inst /etc/shells
     inst /etc/nsswitch.conf
     inst /etc/pam.conf || :
-    inst /etc/securetty || :
     inst /etc/os-release
     inst /etc/localtime
     # we want an empty environment
diff --git a/test/test-network/conf/25-fibrule-uidrange.network b/test/test-network/conf/25-fibrule-uidrange.network
new file mode 100644 (file)
index 0000000..f42dfee
--- /dev/null
@@ -0,0 +1,9 @@
+[Match]
+Name=test1
+
+[RoutingPolicyRule]
+TypeOfService=0x08
+Table=7
+From= 192.168.100.18
+Priority=111
+User=100-200
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..b713245
--- /dev/null
@@ -0,0 +1,12 @@
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[CAKE]
+Parent=root
+Handle=3a
+Overhead=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..039a2ff
--- /dev/null
@@ -0,0 +1,162 @@
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[QDisc]
+Parent=clsact
+
+[HierarchyTokenBucket]
+Parent=root
+Handle=0002
+DefaultClass=30
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0030
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[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
+Quantum=1500
+InitialQuantum=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
+MemoryLimit=64M
+Flows=2048
+TargetSec=10ms
+IntervalSec=200ms
+Quantum=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
+Burst=5K
+LatencySec=70msec
+PeakRate=100G
+MTUBytes=1M
+
+[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
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 20bcca2..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-[Match]
-Name=dummy98
-
-[Network]
-IPv6AcceptRA=no
-Address=10.1.2.3/16
-
-[TrafficControlQueueingDiscipline]
-Parent=root
-FairQueueTrafficPolicingPacketLimit=1000
-FairQueueTrafficPolicingFlowLimit=200
-FairQueueTrafficPolicingQuantum=1500
-FairQueueTrafficPolicingInitialQuantum=13000
-FairQueueTrafficPolicingMaximumRate=1M
-FairQueueTrafficPolicingBuckets=512
-FairQueueTrafficPolicingOrphanMask=511
-FairQueueTrafficPolicingPacing=yes
-FairQueueTrafficPolicingCEThresholdSec=100ms
-
-[TrafficControlQueueingDiscipline]
-Parent=clsact
-ControlledDelayPacketLimit=2000
-ControlledDelayTargetSec=10ms
-ControlledDelayIntervalSec=50ms
-ControlledDelayECN=yes
-ControlledDelayCEThresholdSec=100ms
diff --git a/test/test-network/conf/25-qdisc-ingress-netem-compat.network b/test/test-network/conf/25-qdisc-ingress-netem-compat.network
new file mode 100644 (file)
index 0000000..51dced2
--- /dev/null
@@ -0,0 +1,16 @@
+[Match]
+Name=test1
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.4/16
+
+[TrafficControlQueueingDiscipline]
+Parent=root
+NetworkEmulatorDelaySec=50ms
+NetworkEmulatorDelayJitterSec=10ms
+NetworkEmulatorLossRate=20%
+NetworkEmulatorPacketLimit=100
+
+[TrafficControlQueueingDiscipline]
+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 0e2ad74..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-[Match]
-Name=dummy98
-
-[Network]
-IPv6AcceptRA=no
-Address=10.1.2.3/16
-
-[TrafficControlQueueingDiscipline]
-Parent=root
-NetworkEmulatorDelaySec=50ms
-NetworkEmulatorDelayJitterSec=10ms
-NetworkEmulatorLossRate=20%
-NetworkEmulatorPacketLimit=100
-
-[TrafficControlQueueingDiscipline]
-Parent=ingress
-FairQueuingControlledDelayPacketLimit=20480
-FairQueuingControlledDelayMemoryLimit=64M
-FairQueuingControlledDelayFlows=2048
-FairQueuingControlledDelayTargetSec=10ms
-FairQueuingControlledDelayIntervalSec=200ms
-FairQueuingControlledDelayQuantum=1400
-FairQueuingControlledDelayECN=yes
-FairQueuingControlledDelayCEThresholdSec=100ms
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 781add2..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-[Match]
-Name=test1
-
-[Network]
-IPv6AcceptRA=no
-Address=10.1.2.4/16
-
-[TrafficControlQueueingDiscipline]
-Parent=root
-TokenBufferFilterRate=1G
-TokenBufferFilterBurst=5K
-TokenBufferFilterLatencySec=70msec
-TokenBufferFilterPeakRate=100G
-TokenBufferFilterMTUBytes=1M
-
-[TrafficControlQueueingDiscipline]
-Parent=clsact
-StochasticFairnessQueueingPerturbPeriodSec=5sec
diff --git a/test/test-network/conf/25-route-vrf.network b/test/test-network/conf/25-route-vrf.network
new file mode 100644 (file)
index 0000000..e786066
--- /dev/null
@@ -0,0 +1,7 @@
+[Match]
+Name=dummy98
+
+[Network]
+VRF=vrf99
+Address=192.168.100.2/24
+Gateway=192.168.100.1
index 1b8a3751a438214983584e19e4e7258d34f0f138..7e6f6b33e01e929e44b60c9c86914ca9d25202cc 100644 (file)
@@ -6,5 +6,5 @@ DHCP=ipv4
 IPv6AcceptRA=no
 
 [Route]
-Gateway=dhcp
+Gateway=_dhcp
 Destination=10.0.0.0/8
index 058cb33080a84a7328c76bda507c380757dd3127..ce708e7da9a609663717f68f734650780d9fd1b2 100644 (file)
@@ -5,5 +5,5 @@ Name=veth99
 DHCP=ipv6
 
 [Route]
-Gateway=dhcp
+Gateway=_dhcp
 Destination=2001:1234:5:9fff:ff:ff:ff:ff/128
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
new file mode 100644 (file)
index 0000000..21e21fa
--- /dev/null
@@ -0,0 +1,9 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=false
+
+[DHCPv4]
+UseRoutes=no
diff --git a/test/test-network/conf/ipv6-prefix-veth-token-prefixstable.network b/test/test-network/conf/ipv6-prefix-veth-token-prefixstable.network
new file mode 100644 (file)
index 0000000..4d80e02
--- /dev/null
@@ -0,0 +1,6 @@
+[Match]
+Name=veth99
+
+[Network]
+IPv6AcceptRA=true
+IPv6Token=prefixstable:2002:da8:1::
diff --git a/test/test-network/conf/ipv6-prefix-veth-token-static-explicit.network b/test/test-network/conf/ipv6-prefix-veth-token-static-explicit.network
new file mode 100644 (file)
index 0000000..237f9aa
--- /dev/null
@@ -0,0 +1,6 @@
+[Match]
+Name=veth99
+
+[Network]
+IPv6AcceptRA=true
+IPv6Token=static:::1a:2b:3c:4d
diff --git a/test/test-network/conf/ipv6-prefix-veth-token-static-multiple.network b/test/test-network/conf/ipv6-prefix-veth-token-static-multiple.network
new file mode 100644 (file)
index 0000000..49c6f31
--- /dev/null
@@ -0,0 +1,7 @@
+[Match]
+Name=veth99
+
+[Network]
+IPv6AcceptRA=true
+IPv6Token=::1a:2b:3c:4d
+IPv6Token=::fa:de:ca:fe
diff --git a/test/test-network/conf/ipv6-prefix-veth-token-static.network b/test/test-network/conf/ipv6-prefix-veth-token-static.network
new file mode 100644 (file)
index 0000000..1e43bd4
--- /dev/null
@@ -0,0 +1,6 @@
+[Match]
+Name=veth99
+
+[Network]
+IPv6AcceptRA=true
+IPv6Token=::1a:2b:3c:4d
index c1932a84d389400a004ad5bc74b55ff38a7c4062..215cc9071bc8689af01b79359e1ab14cde623d35 100644 (file)
@@ -4,6 +4,10 @@ Name=veth-peer
 [Network]
 IPv6PrefixDelegation=yes
 
+[IPv6PrefixDelegation]
+DNS=_link_local 2002:da8:1:0::1
+DNSLifetimeSec=1min
+
 [IPv6Prefix]
 Prefix=2002:da8:1:0::/64
 PreferredLifetimeSec=1000s
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 f45e948b682a1ffbb6eb935c012c20cd42804113..fb0048b3e6af126009a2fc38654059db21dd9669 100755 (executable)
@@ -27,6 +27,7 @@ which_paths=':'.join(systemd_lib_paths + os.getenv('PATH', os.defpath).lstrip(':
 
 networkd_bin=shutil.which('systemd-networkd', path=which_paths)
 resolved_bin=shutil.which('systemd-resolved', path=which_paths)
+udevd_bin=shutil.which('systemd-udevd', path=which_paths)
 wait_online_bin=shutil.which('systemd-networkd-wait-online', path=which_paths)
 networkctl_bin=shutil.which('networkctl', path=which_paths)
 resolvectl_bin=shutil.which('resolvectl', path=which_paths)
@@ -100,6 +101,23 @@ def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable():
 
     return f
 
+def expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable():
+    def f(func):
+        support = False
+        rc = call('ip rule add from 192.168.100.19 table 7 uidrange 200-300', stderr=subprocess.DEVNULL)
+        if rc == 0:
+            ret = run('ip rule list from 192.168.100.19 table 7', stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+            if ret.returncode == 0 and 'uidrange 200-300' in ret.stdout.rstrip():
+                support = True
+            call('ip rule del from 192.168.100.19 table 7 uidrange 200-300')
+
+        if support:
+            return func
+        else:
+            return unittest.expectedFailure(func)
+
+    return f
+
 def expectedFailureIfLinkFileFieldIsNotSet():
     def f(func):
         support = False
@@ -131,6 +149,19 @@ def expectedFailureIfAlternativeNameIsNotAvailable():
     def f(func):
         call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
         rc = call('ip link prop add dev dummy98 altname hogehogehogehogehoge', stderr=subprocess.DEVNULL)
+        call('ip link del dummy98', stderr=subprocess.DEVNULL)
+        if rc == 0:
+            return func
+        else:
+            return unittest.expectedFailure(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:
@@ -147,7 +178,9 @@ def setUpModule():
     shutil.rmtree(networkd_ci_path)
     copytree(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf'), networkd_ci_path)
 
-    for u in ['systemd-networkd.socket', 'systemd-networkd.service', 'systemd-resolved.service', 'firewalld.service']:
+    for u in ['systemd-networkd.socket', 'systemd-networkd.service', 'systemd-resolved.service',
+              'systemd-udevd-kernel.socket', 'systemd-udevd-control.socket', 'systemd-udevd.service',
+              'firewalld.service']:
         if call(f'systemctl is-active --quiet {u}') == 0:
             check_output(f'systemctl stop {u}')
             running_units.append(u)
@@ -209,10 +242,22 @@ def setUpModule():
     with open('/run/systemd/system/systemd-resolved.service.d/00-override.conf', mode='w') as f:
         f.write('\n'.join(drop_in))
 
+    drop_in = [
+        '[Service]',
+        'ExecStart=',
+        'ExecStart=!!' + udevd_bin,
+    ]
+
+    os.makedirs('/run/systemd/system/systemd-udevd.service.d', exist_ok=True)
+    with open('/run/systemd/system/systemd-udevd.service.d/00-override.conf', mode='w') as f:
+        f.write('\n'.join(drop_in))
+
     check_output('systemctl daemon-reload')
     print(check_output('systemctl cat systemd-networkd.service'))
     print(check_output('systemctl cat systemd-resolved.service'))
+    print(check_output('systemctl cat systemd-udevd.service'))
     check_output('systemctl restart systemd-resolved')
+    check_output('systemctl restart systemd-udevd')
 
 def tearDownModule():
     global running_units
@@ -224,7 +269,9 @@ def tearDownModule():
 
     shutil.rmtree('/run/systemd/system/systemd-networkd.service.d')
     shutil.rmtree('/run/systemd/system/systemd-resolved.service.d')
+    shutil.rmtree('/run/systemd/system/systemd-udevd.service.d')
     check_output('systemctl daemon-reload')
+    check_output('systemctl restart systemd-udevd.service')
 
     for u in running_units:
         check_output(f'systemctl start {u}')
@@ -1555,6 +1602,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         'ip6gretun97',
         'test1',
         'veth99',
+        'vrf99',
     ]
 
     units = [
@@ -1572,6 +1620,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         '25-bond-active-backup-slave.netdev',
         '25-fibrule-invert.network',
         '25-fibrule-port-range.network',
+        '25-fibrule-uidrange.network',
         '25-gre-tunnel-remote-any.netdev',
         '25-ip6gre-tunnel-remote-any.netdev',
         '25-ipv6-address-label-section.network',
@@ -1584,17 +1633,19 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         '25-neighbor-ip-dummy.network',
         '25-neighbor-ip.network',
         '25-nexthop.network',
-        '25-qdisc-fq-codel.network',
-        '25-qdisc-netem-and-fqcodel.network',
-        '25-qdisc-tbf-and-sfq.network',
+        '25-qdisc-cake.network',
+        '25-qdisc-clsact-and-htb.network',
+        '25-qdisc-ingress-netem-compat.network',
         '25-route-ipv6-src.network',
         '25-route-static.network',
+        '25-route-vrf.network',
         '25-gateway-static.network',
         '25-gateway-next-static.network',
         '25-sysctl-disable-ipv6.network',
         '25-sysctl.network',
         '25-veth-peer.network',
         '25-veth.netdev',
+        '25-vrf.netdev',
         '26-link-local-addressing-ipv6.network',
         'configure-without-carrier.network',
         'routing-policy-rule-dummy98.network',
@@ -1776,6 +1827,19 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'tcp')
         self.assertRegex(output, 'lookup 7')
 
+    @expectedFailureIfRoutingPolicyUIDRangeIsNotAvailable()
+    def test_routing_policy_rule_uidrange(self):
+        copy_unit_to_networkd_unit_path('25-fibrule-uidrange.network', '11-dummy.netdev')
+        start_networkd()
+        self.wait_online(['test1:degraded'])
+
+        output = check_output('ip rule')
+        print(output)
+        self.assertRegex(output, '111')
+        self.assertRegex(output, 'from 192.168.100.18')
+        self.assertRegex(output, 'lookup 7')
+        self.assertRegex(output, 'uidrange 100-200')
+
     def test_route_static(self):
         copy_unit_to_networkd_unit_path('25-route-static.network', '12-dummy.netdev')
         start_networkd()
@@ -1858,6 +1922,21 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98')
         self.assertRegex(output, 'via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98')
 
+    @expectedFailureIfModuleIsNotAvailable('vrf')
+    def test_route_vrf(self):
+        copy_unit_to_networkd_unit_path('25-route-vrf.network', '12-dummy.netdev',
+                                        '25-vrf.netdev', '25-vrf.network')
+        start_networkd()
+        self.wait_online(['dummy98:routable', 'vrf99:carrier'])
+
+        output = check_output('ip route show vrf vrf99')
+        print(output)
+        self.assertRegex(output, 'default via 192.168.100.1')
+
+        output = check_output('ip route show')
+        print(output)
+        self.assertNotRegex(output, 'default via 192.168.100.1')
+
     def test_gateway_reconfigure(self):
         copy_unit_to_networkd_unit_path('25-gateway-static.network', '12-dummy.netdev')
         start_networkd()
@@ -2183,37 +2262,85 @@ 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 dummy98')
+        output = check_output('tc qdisc show dev test1')
         print(output)
         self.assertRegex(output, 'qdisc netem')
         self.assertRegex(output, 'limit 100 delay 50.0ms  10.0ms loss 20%')
+        self.assertRegex(output, 'qdisc ingress')
+
+        output = check_output('tc qdisc show dev dummy98')
+        print(output)
+        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.0ms  10.0ms 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')
+
+        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.0ms ce_threshold 100.0ms interval 50.0ms ecn')
+
+        self.assertRegex(output, 'qdisc fq_codel 34: parent 2:34')
+        self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10.0ms ce_threshold 100.0ms interval 200.0ms 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.0ms')
-        self.assertRegex(output, 'qdisc sfq')
+
+        self.assertRegex(output, 'qdisc sfq 36: parent 2:36')
         self.assertRegex(output, 'perturb 5sec')
 
-    def test_qdisc2(self):
-        copy_unit_to_networkd_unit_path('25-qdisc-fq-codel.network', '12-dummy.netdev')
-        start_networkd()
+        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')
+
+        output = check_output('tc 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, 'prio 1 rate 1Mbit ceil 500Kbit')
 
+    @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'])
 
         output = check_output('tc qdisc show dev dummy98')
         print(output)
-        self.assertRegex(output, 'qdisc fq')
-        self.assertRegex(output, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511 quantum 1500 initial_quantum 13000 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 cake 3a: root')
+        self.assertRegex(output, 'bandwidth 500Mbit')
+        self.assertRegex(output, 'overhead 128')
 
 class NetworkdStateFileTests(unittest.TestCase, Utilities):
     links = [
@@ -2596,7 +2723,11 @@ class NetworkdRATests(unittest.TestCase, Utilities):
     units = [
         '25-veth.netdev',
         'ipv6-prefix.network',
-        'ipv6-prefix-veth.network']
+        'ipv6-prefix-veth.network',
+        'ipv6-prefix-veth-token-static.network',
+        'ipv6-prefix-veth-token-static-explicit.network',
+        'ipv6-prefix-veth-token-static-multiple.network',
+        'ipv6-prefix-veth-token-prefixstable.network']
 
     def setUp(self):
         remove_links(self.links)
@@ -2612,6 +2743,48 @@ class NetworkdRATests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['veth99:routable', 'veth-peer:degraded'])
 
+        output = check_output(*resolvectl_cmd, 'dns', 'veth99', env=env)
+        print(output)
+        self.assertRegex(output, 'fe80::')
+        self.assertRegex(output, '2002:da8:1::1')
+
+        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
+        print(output)
+        self.assertRegex(output, '2002:da8:1:0')
+
+    def test_ipv6_token_static(self):
+        copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth-token-static.network')
+        start_networkd()
+        self.wait_online(['veth99:routable', 'veth-peer:degraded'])
+
+        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
+        print(output)
+        self.assertRegex(output, '2002:da8:1:0:1a:2b:3c:4d')
+
+    def test_ipv6_token_static_explicit(self):
+        copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth-token-static-explicit.network')
+        start_networkd()
+        self.wait_online(['veth99:routable', 'veth-peer:degraded'])
+
+        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
+        print(output)
+        self.assertRegex(output, '2002:da8:1:0:1a:2b:3c:4d')
+
+    def test_ipv6_token_static_multiple(self):
+        copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth-token-static-multiple.network')
+        start_networkd()
+        self.wait_online(['veth99:routable', 'veth-peer:degraded'])
+
+        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
+        print(output)
+        self.assertRegex(output, '2002:da8:1:0:1a:2b:3c:4d')
+        self.assertRegex(output, '2002:da8:1:0:fa:de:ca:fe')
+
+    def test_ipv6_token_prefixstable(self):
+        copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth-token-prefixstable.network')
+        start_networkd()
+        self.wait_online(['veth99:routable', 'veth-peer:degraded'])
+
         output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
         print(output)
         self.assertRegex(output, '2002:da8:1:0')
@@ -2675,6 +2848,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-ipv6-only.network',
         'dhcp-client-ipv6-rapid-commit.network',
         'dhcp-client-keep-configuration-dhcp-on-stop.network',
@@ -2779,6 +2953,20 @@ 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')
+
+        start_networkd()
+        self.wait_online(['veth-peer:carrier'])
+        start_dnsmasq(additional_options='--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7', lease_time='2m')
+        self.wait_online(['veth99:routable', 'veth-peer:routable'])
+
+        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')
+
     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',
                                         'dhcp-client-ipv4-only.network')
@@ -3425,10 +3613,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']
 
@@ -3529,6 +3722,7 @@ if __name__ == '__main__':
     parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir')
     parser.add_argument('--networkd', help='Path to systemd-networkd', dest='networkd_bin')
     parser.add_argument('--resolved', help='Path to systemd-resolved', dest='resolved_bin')
+    parser.add_argument('--udevd', help='Path to systemd-udevd', dest='udevd_bin')
     parser.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest='wait_online_bin')
     parser.add_argument('--networkctl', help='Path to networkctl', dest='networkctl_bin')
     parser.add_argument('--resolvectl', help='Path to resolvectl', dest='resolvectl_bin')
@@ -3541,10 +3735,11 @@ if __name__ == '__main__':
     ns, args = parser.parse_known_args(namespace=unittest)
 
     if ns.build_dir:
-        if ns.networkd_bin or ns.resolved_bin or ns.wait_online_bin or ns.networkctl_bin or ns.resolvectl_bin or ns.timedatectl_bin:
+        if ns.networkd_bin or ns.resolved_bin or ns.udevd_bin or ns.wait_online_bin or ns.networkctl_bin or ns.resolvectl_bin or ns.timedatectl_bin:
             print('WARNING: --networkd, --resolved, --wait-online, --networkctl, --resolvectl, or --timedatectl options are ignored when --build-dir is specified.')
         networkd_bin = os.path.join(ns.build_dir, 'systemd-networkd')
         resolved_bin = os.path.join(ns.build_dir, 'systemd-resolved')
+        udevd_bin = os.path.join(ns.build_dir, 'systemd-udevd')
         wait_online_bin = os.path.join(ns.build_dir, 'systemd-networkd-wait-online')
         networkctl_bin = os.path.join(ns.build_dir, 'networkctl')
         resolvectl_bin = os.path.join(ns.build_dir, 'resolvectl')
@@ -3554,6 +3749,8 @@ if __name__ == '__main__':
             networkd_bin = ns.networkd_bin
         if ns.resolved_bin:
             resolved_bin = ns.resolved_bin
+        if ns.udevd_bin:
+            udevd_bin = ns.udevd_bin
         if ns.wait_online_bin:
             wait_online_bin = ns.wait_online_bin
         if ns.networkctl_bin:
index 4914e8ec8c6be239e1700d5aa69eeccf73d4f005..11d87d275b743b82387aa1474771a9e9fde7b26d 100644 (file)
@@ -32,17 +32,17 @@ Z /run/log/journal/%m ~2750 root systemd-journal - -
 m4_ifdef(`HAVE_ACL',`m4_dnl
 m4_ifdef(`ENABLE_ADM_GROUP',`m4_dnl
 m4_ifdef(`ENABLE_WHEEL_GROUP',``
-a+ /run/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x
-a+ /run/log/journal/%m - - - - group:adm:r-x,group:wheel:r-x
+a+ /run/log/journal    - - - - d:group::r-x,d:group:adm:r-x,d:group:wheel:r-x,group::r-x,group:adm:r-x,group:wheel:r-x
+a+ /run/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x,group:adm:r-x,group:wheel:r-x
 a+ /run/log/journal/%m/*.journal* - - - - group:adm:r--,group:wheel:r--
 '',``
-a+ /run/log/journal/%m - - - - d:group:adm:r-x
-a+ /run/log/journal/%m - - - - group:adm:r-x
+a+ /run/log/journal    - - - - d:group::r-x,d:group:adm:r-x,group::r-x,group:adm:r-x
+a+ /run/log/journal/%m - - - - d:group:adm:r-x,group:adm:r-x
 a+ /run/log/journal/%m/*.journal* - - - - group:adm:r--
 '')',`m4_dnl
 m4_ifdef(`ENABLE_WHEEL_GROUP',``
-a+ /run/log/journal/%m - - - - d:group:wheel:r-x
-a+ /run/log/journal/%m - - - - group:wheel:r-x
+a+ /run/log/journal    - - - - d:group::r-x,d:group:wheel:r-x,group::r-x,group:wheel:r-x
+a+ /run/log/journal/%m - - - - d:group:wheel:r-x,group:wheel:r-x
 a+ /run/log/journal/%m/*.journal* - - - - group:wheel:r--
 '')')')m4_dnl
 
@@ -52,23 +52,17 @@ z /var/log/journal/%m/system.journal 0640 root systemd-journal - -
 m4_ifdef(`HAVE_ACL',`m4_dnl
 m4_ifdef(`ENABLE_ADM_GROUP',`m4_dnl
 m4_ifdef(`ENABLE_WHEEL_GROUP',``
-a+ /var/log/journal    - - - - d:group::r-x,d:group:adm:r-x,d:group:wheel:r-x
-a+ /var/log/journal    - - - - group::r-x,group:adm:r-x,group:wheel:r-x
-a+ /var/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x
-a+ /var/log/journal/%m - - - - group:adm:r-x,group:wheel:r-x
+a+ /var/log/journal    - - - - d:group::r-x,d:group:adm:r-x,d:group:wheel:r-x,group::r-x,group:adm:r-x,group:wheel:r-x
+a+ /var/log/journal/%m - - - - d:group:adm:r-x,d:group:wheel:r-x,group:adm:r-x,group:wheel:r-x
 a+ /var/log/journal/%m/system.journal - - - - group:adm:r--,group:wheel:r--
 '', ``
-a+ /var/log/journal    - - - - d:group::r-x,d:group:adm:r-x
-a+ /var/log/journal    - - - - group::r-x,group:adm:r-x
-a+ /var/log/journal/%m - - - - d:group:adm:r-x
-a+ /var/log/journal/%m - - - - group:adm:r-x
+a+ /var/log/journal    - - - - d:group::r-x,d:group:adm:r-x,group::r-x,group:adm:r-x
+a+ /var/log/journal/%m - - - - d:group:adm:r-x,group:adm:r-x
 a+ /var/log/journal/%m/system.journal - - - - group:adm:r--
 '')',`m4_dnl
 m4_ifdef(`ENABLE_WHEEL_GROUP',``
-a+ /var/log/journal    - - - - d:group::r-x,d:group:wheel:r-x
-a+ /var/log/journal    - - - - group::r-x,group:wheel:r-x
-a+ /var/log/journal/%m - - - - d:group:wheel:r-x
-a+ /var/log/journal/%m - - - - group:wheel:r-x
+a+ /var/log/journal    - - - - d:group::r-x,d:group:wheel:r-x,group::r-x,group:wheel:r-x
+a+ /var/log/journal/%m - - - - d:group:wheel:r-x,group:wheel:r-x
 a+ /var/log/journal/%m/system.journal - - - - group:wheel:r--
 '')')')m4_dnl
 
index a90fb604ae0999d6ba3a7907a7c6a50eac7d5c15..1a0bb0995be12920648354ddb111e108215a9d7d 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
 
 which perl &>/dev/null || exit 77
index e50d6e743aad996fd14c7bdf8d95100ba02bf17b..41e6cc95862bef8b68b0538675f87e91ac273ec6 100644 (file)
@@ -34,6 +34,8 @@ USB_IDS += [
     '8087:0024',
     # Genesys Logic (Internal Hub) (rambi)
     '8087:8000',
+    # Microchip (Composite HID + CDC) (kefka)
+    '04d8:0b28',
 ]
 
 # Webcams
@@ -98,6 +100,8 @@ USB_IDS += [
     '04ca:3016',
     # LiteOn (scarlet)
     '04ca:301a',
+    # Realtek (blooglet)
+    '0bda:b00c',
     # Atheros (stumpy, stout)
     '0cf3:3004',
     # Atheros (AR3011) (mario, alex, zgb)
@@ -236,6 +240,21 @@ PCI_IDS += [
     '8086:591c',
     # iwlwifi (atlas)
     '8086:2526',
+    # i915 (kefka)
+    '8086:22b1',
+    # proc_thermal (kefka)
+    '8086:22dc',
+    # xchi_hdc (kefka)
+    '8086:22b5',
+    # snd_hda (kefka)
+    '8086:2284',
+    # pcieport (kefka)
+    '8086:22c8',
+    '8086:22cc',
+    # lpc_ich (kefka)
+    '8086:229c',
+    # iosf_mbi_pci (kefka)
+    '8086:2280',
 ]
 
 # Samsung
@@ -264,7 +283,7 @@ PCI_IDS += [
     '2646:5008',
 ]
 
-################################################################################
+# Do not edit below this line. #################################################
 
 UDEV_RULE = """\
 ACTION!="add", GOTO="autosuspend_end"
index 8edce065caeaf6d5b99df1b4bc44d711106ded5f..5d3b7e2918074d26cf7586a4ae0685b5b6e7c585 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 # The official unmodified version of the script can be found at
 # https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh
index 5dc95fa8c15ad4fd61a84c5ade936010c8594482..1c3814d4830b3c523e0fc1cedd7e4c5e947bf98d 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 
 set -eu
 set -o pipefail
index 6884048d83e28a5703749ce9510220113e9f351a..79846f87879cfadc7be758e29fbf669ce0983cec 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
 # SPDX-License-Identifier: LGPL-2.1+
 
 set -ex
@@ -32,10 +32,6 @@ if [ -z "$FUZZING_ENGINE" ]; then
     fuzzflag="llvm-fuzz=true"
 fi
 
-# FIXME: temporarily pin the meson version as 0.53 doesn't work with older python 3.5
-# See: https://github.com/mesonbuild/meson/issues/6427
-pip3 install meson==0.52.1
-
 meson $build -D$fuzzflag -Db_lundef=false
 ninja -v -C $build fuzzers
 
index 6a6923fbc86a138d7623220f19c8c1724d17dfe2..ac86e6274e0c86f2643a9abd3c44f758971d3a0f 100755 (executable)
@@ -18,7 +18,11 @@ REPO_ROOT="${REPO_ROOT:-$PWD}"
 ADDITIONAL_DEPS=(python3-libevdev
                  python3-pyparsing
                  clang
-                 perl)
+                 perl
+                 libpwquality-dev
+                 libfdisk-dev
+                 libp11-kit-dev
+                 libssl-dev)
 
 function info() {
     echo -e "\033[33;1m$1\033[0m"
index e07b4938dfd47a5eabcda2d3cf46f7f522b34d4d..b0f431aac9e15191a526280b69321a779f905572 100755 (executable)
@@ -23,7 +23,11 @@ ADDITIONAL_DEPS=(dnf-plugins-core
                  libubsan
                  clang
                  llvm
-                 perl)
+                 perl
+                 libfdisk-devel
+                 libpwquality-devel
+                 openssl-devel
+                 p11-kit-devel)
 
 function info() {
     echo -e "\033[33;1m$1\033[0m"
index 1541b466526681ec65eb99f243c71613db606631..c841af121471ab492e868d8ecf30fe77230963c1 100755 (executable)
@@ -10,14 +10,14 @@ sudo bash -c "echo 'deb-src http://archive.ubuntu.com/ubuntu/ xenial main restri
 sudo apt-get update -y
 sudo apt-get build-dep systemd -y
 sudo apt-get install -y ninja-build python3-pip python3-setuptools quota
-# FIXME: temporarily pin the meson version as 0.53 doesn't work with older python 3.5
-# See: https://github.com/mesonbuild/meson/issues/6427
-pip3 install meson==0.52.1
+# 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"
 tools/oss-fuzz.sh
-timeout --preserve-status 5 ./out/fuzz-unit-file
+./out/fuzz-unit-file -max_total_time=5
 git clean -dxff
 
 wget https://app.fuzzbuzz.io/releases/cli/latest/linux/fuzzbuzz
index 376761e20ccfa7a7da1fe90bfaf0d69ca4be00ed..c3d76134fec4a5f97dbb0d4acf9cda9b26fd4cc3 100755 (executable)
@@ -14,9 +14,9 @@ sudo bash -c "echo 'deb-src http://archive.ubuntu.com/ubuntu/ xenial main restri
 sudo apt-get update -y
 sudo apt-get build-dep systemd -y
 sudo apt-get install -y ninja-build python3-pip python3-setuptools
-# FIXME: temporarily pin the meson version as 0.53 doesn't work with older python 3.5
-# See: https://github.com/mesonbuild/meson/issues/6427
-pip3 install meson==0.52.1
+# 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"
diff --git a/units/blockdev@.target b/units/blockdev@.target
new file mode 100644 (file)
index 0000000..22a9a5b
--- /dev/null
@@ -0,0 +1,13 @@
+#  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=Block Device Preparation for %f
+Documentation=man:systemd.special(7)
+StopWhenUnneeded=yes
similarity index 88%
rename from units/initrd-cleanup.service.in
rename to units/initrd-cleanup.service
index 9775540c947b0cb18e5787221c22801300a4920a..b04607671503fcf043c11ba32c9daffe971de330 100644 (file)
@@ -17,4 +17,4 @@ After=initrd-root-fs.target initrd-fs.target initrd.target
 
 [Service]
 Type=oneshot
-ExecStart=@rootbindir@/systemctl --no-block isolate initrd-switch-root.target
+ExecStart=systemctl --no-block isolate initrd-switch-root.target
index 33822bde66b9ec3e761046f4512a5c589e6a473c..1db8fb3a48d59d1684df3eb024aef1da6148d02f 100644 (file)
@@ -10,8 +10,6 @@
 [Unit]
 Description=Initrd File Systems
 Documentation=man:systemd.special(7)
-OnFailure=emergency.target
-OnFailureJobMode=replace-irreversibly
 ConditionPathExists=/etc/initrd-release
 After=initrd-parse-etc.service
 DefaultDependencies=no
similarity index 77%
rename from units/initrd-parse-etc.service.in
rename to units/initrd-parse-etc.service
index 2b3cd61cd6014f4cebc637fb237b34dc18a8e2f4..b0e38368cfbd3782d861675c8b97a707c68c3850 100644 (file)
@@ -18,7 +18,7 @@ ConditionPathExists=/etc/initrd-release
 
 [Service]
 Type=oneshot
-ExecStartPre=-@rootbindir@/systemctl daemon-reload
+ExecStartPre=-systemctl daemon-reload
 # we have to retrigger initrd-fs.target after daemon-reload
-ExecStart=-@rootbindir@/systemctl --no-block start initrd-fs.target
-ExecStart=@rootbindir@/systemctl --no-block start initrd-cleanup.service
+ExecStart=-systemctl --no-block start initrd-fs.target
+ExecStart=systemctl --no-block start initrd-cleanup.service
index 580c666b2310fcde64ac3a9d5c234e1f978c6fb0..bc2743e2d5438ef8407b3ca47652a32c104f78df 100644 (file)
@@ -11,7 +11,5 @@
 Description=Initrd Root Device
 Documentation=man:systemd.special(7)
 ConditionPathExists=/etc/initrd-release
-OnFailure=emergency.target
-OnFailureJobMode=replace-irreversibly
 DefaultDependencies=no
 Conflicts=shutdown.target
index 9b955f618a5fb080c73e2c989958988c97a6d8e1..26897796af627ac41a3b00b2ab3ca99fad5ed660 100644 (file)
@@ -11,7 +11,5 @@
 Description=Initrd Root File System
 Documentation=man:systemd.special(7)
 ConditionPathExists=/etc/initrd-release
-OnFailure=emergency.target
-OnFailureJobMode=replace-irreversibly
 DefaultDependencies=no
 Conflicts=shutdown.target
similarity index 89%
rename from units/initrd-switch-root.service.in
rename to units/initrd-switch-root.service
index 6ce468e872c83dfadd595f419c0d2daf3c6fc9aa..c1a37928880535435318a74e982ec76f8e385bd6 100644 (file)
@@ -17,4 +17,4 @@ AllowIsolate=yes
 
 [Service]
 Type=oneshot
-ExecStart=@rootbindir@/systemctl --no-block switch-root /sysroot
+ExecStart=systemctl --no-block switch-root /sysroot
similarity index 93%
rename from units/initrd-udevadm-cleanup-db.service.in
rename to units/initrd-udevadm-cleanup-db.service
index 09af690986c9b5b929b1ee26624d67dcb926eba7..ad2f2a5b35b74d277ed0f1a2666705f7ba4f38b5 100644 (file)
@@ -17,4 +17,4 @@ Before=initrd-switch-root.target
 
 [Service]
 Type=oneshot
-ExecStart=-@rootbindir@/udevadm info --cleanup-db
+ExecStart=-udevadm info --cleanup-db
index a74a447c91ec0ce530c9dae61b9e59f54a862720..30b3bd04d2ee59c7f4a8acdee917dcf3d811fa3d 100644 (file)
@@ -10,8 +10,6 @@
 [Unit]
 Description=Initrd Default Target
 Documentation=man:systemd.special(7)
-OnFailure=emergency.target
-OnFailureJobMode=replace-irreversibly
 ConditionPathExists=/etc/initrd-release
 Requires=basic.target
 Wants=initrd-root-fs.target initrd-root-device.target initrd-fs.target initrd-parse-etc.service
index 6ba49300870be220296c337ee4832b43af29a390..da3683e948a96f6f982070ce8d9c7bcfe844a665 100644 (file)
@@ -13,5 +13,3 @@ Documentation=man:systemd.special(7)
 DefaultDependencies=no
 Conflicts=shutdown.target
 After=local-fs-pre.target
-OnFailure=emergency.target
-OnFailureJobMode=replace-irreversibly
index 4ad64d12f255f7a6fc4cb3413cc48dbadab656a6..ea91f0cc9ea7df63e5e75d6a7e096e06ffd7d46f 100644 (file)
@@ -2,6 +2,7 @@
 
 units = [
         ['basic.target',                        ''],
+        ['blockdev@.target',                    ''],
         ['bluetooth.target',                    ''],
         ['boot-complete.target',                ''],
         ['cryptsetup-pre.target',               'HAVE_LIBCRYPTSETUP'],
@@ -23,10 +24,14 @@ units = [
         ['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',                       ''],
         ['kexec.target',                        ''],
         ['ldconfig.service',                    'ENABLE_LDCONFIG',
@@ -73,6 +78,8 @@ units = [
          'sysinit.target.wants/'],
         ['sys-kernel-debug.mount',              '',
          'sysinit.target.wants/'],
+        ['sys-kernel-tracing.mount',            '',
+         'sysinit.target.wants/'],
         ['sysinit.target',                      ''],
         ['syslog.socket',                       ''],
         ['system-systemd\\x2dcryptsetup.slice', 'HAVE_LIBCRYPTSETUP'],
@@ -81,13 +88,24 @@ units = [
         ['system-update-cleanup.service',       ''],
         ['systemd-ask-password-console.path',   '',
          'sysinit.target.wants/'],
+        ['systemd-ask-password-console.service', ''],
         ['systemd-ask-password-wall.path',      '',
          'multi-user.target.wants/'],
+        ['systemd-ask-password-wall.service',   ''],
+        ['systemd-boot-system-token.service',   'ENABLE_EFI',
+         'sysinit.target.wants/'],
         ['systemd-coredump.socket',             'ENABLE_COREDUMP',
          'sockets.target.wants/'],
-        ['systemd-exit.service',                 ''],
+        ['systemd-exit.service',                ''],
+        ['systemd-firstboot.service',           'ENABLE_FIRSTBOOT',
+         'sysinit.target.wants/'],
+        ['systemd-halt.service',                ''],
         ['systemd-initctl.socket',              '',
          'sockets.target.wants/'],
+        ['systemd-journal-catalog-update.service', '',
+         'sysinit.target.wants/'],
+        ['systemd-journal-flush.service',       '',
+         'sysinit.target.wants/'],
         ['systemd-journal-gatewayd.socket',     'ENABLE_REMOTE HAVE_MICROHTTPD'],
         ['systemd-journal-remote.socket',       'ENABLE_REMOTE HAVE_MICROHTTPD'],
         ['systemd-journald-audit.socket',       '',
@@ -96,16 +114,33 @@ units = [
          'sockets.target.wants/'],
         ['systemd-journald.socket',             '',
          'sockets.target.wants/'],
+        ['systemd-kexec.service',               ''],
+        ['systemd-machine-id-commit.service',   '',
+         'sysinit.target.wants/'],
+        ['systemd-journald@.socket',            ''],
+        ['systemd-journald-varlink@.socket',    ''],
         ['systemd-networkd.socket',             'ENABLE_NETWORKD'],
-        ['systemd-poweroff.service',             ''],
-        ['systemd-reboot.service',               ''],
+        ['systemd-poweroff.service',            ''],
+        ['systemd-reboot.service',              ''],
         ['systemd-rfkill.socket',               'ENABLE_RFKILL'],
+        ['systemd-sysusers.service',            'ENABLE_SYSUSERS',
+         'sysinit.target.wants/'],
+        ['systemd-tmpfiles-clean.service',      'ENABLE_TMPFILES'],
         ['systemd-tmpfiles-clean.timer',        'ENABLE_TMPFILES',
          'timers.target.wants/'],
+        ['systemd-tmpfiles-setup-dev.service',  'ENABLE_TMPFILES',
+         'sysinit.target.wants/'],
+        ['systemd-tmpfiles-setup.service',      'ENABLE_TMPFILES',
+         'sysinit.target.wants/'],
         ['systemd-udevd-control.socket',        '',
          'sockets.target.wants/'],
+        ['systemd-udev-settle.service',         ''],
+        ['systemd-udev-trigger.service',        '',
+         'sysinit.target.wants/'],
         ['systemd-udevd-kernel.socket',         '',
          'sockets.target.wants/'],
+        ['systemd-userdbd.socket',              'ENABLE_USERDB',
+         'sockets.target.wants/'],
         ['time-set.target',                     ''],
         ['time-sync.target',                    ''],
         ['timers.target',                       ''],
@@ -120,31 +155,20 @@ units = [
 in_units = [
         ['debug-shell.service',                  ''],
         ['emergency.service',                    ''],
-        ['initrd-cleanup.service',               ''],
-        ['initrd-parse-etc.service',             ''],
-        ['initrd-switch-root.service',           ''],
-        ['initrd-udevadm-cleanup-db.service',    ''],
         ['kmod-static-nodes.service',            'HAVE_KMOD ENABLE_TMPFILES',
          'sysinit.target.wants/'],
         ['quotaon.service',                      'ENABLE_QUOTACHECK'],
         ['rc-local.service',                     'HAVE_SYSV_COMPAT'],
         ['rescue.service',                       ''],
-        ['systemd-ask-password-console.service', ''],
-        ['systemd-ask-password-wall.service',    ''],
         ['systemd-backlight@.service',           'ENABLE_BACKLIGHT'],
         ['systemd-binfmt.service',               'ENABLE_BINFMT',
          'sysinit.target.wants/'],
         ['systemd-bless-boot.service',           'ENABLE_EFI HAVE_BLKID'],
         ['systemd-boot-check-no-failures.service', ''],
-        ['systemd-boot-system-token.service',    'ENABLE_EFI',
-         'sysinit.target.wants/'],
         ['systemd-coredump@.service',            'ENABLE_COREDUMP'],
         ['systemd-pstore.service',               'ENABLE_PSTORE'],
-        ['systemd-firstboot.service',            'ENABLE_FIRSTBOOT',
-         'sysinit.target.wants/'],
         ['systemd-fsck-root.service',            ''],
         ['systemd-fsck@.service',                ''],
-        ['systemd-halt.service',                 ''],
         ['systemd-hibernate-resume@.service',    'ENABLE_HIBERNATE'],
         ['systemd-hibernate.service',            'ENABLE_HIBERNATE'],
         ['systemd-hybrid-sleep.service',         'ENABLE_HIBERNATE'],
@@ -156,22 +180,16 @@ in_units = [
         ['systemd-importd.service',              'ENABLE_IMPORTD',
          'dbus-org.freedesktop.import1.service'],
         ['systemd-initctl.service',               ''],
-        ['systemd-journal-catalog-update.service', '',
-         'sysinit.target.wants/'],
-        ['systemd-journal-flush.service',         '',
-         'sysinit.target.wants/'],
         ['systemd-journal-gatewayd.service',     'ENABLE_REMOTE HAVE_MICROHTTPD'],
         ['systemd-journal-remote.service',       'ENABLE_REMOTE HAVE_MICROHTTPD'],
         ['systemd-journal-upload.service',       'ENABLE_REMOTE HAVE_LIBCURL'],
         ['systemd-journald.service',             '',
          'sysinit.target.wants/'],
-        ['systemd-kexec.service',                ''],
+        ['systemd-journald@.service',            ''],
         ['systemd-localed.service',              'ENABLE_LOCALED',
          'dbus-org.freedesktop.locale1.service'],
         ['systemd-logind.service',               'ENABLE_LOGIND',
          'multi-user.target.wants/ dbus-org.freedesktop.login1.service'],
-        ['systemd-machine-id-commit.service',    '',
-         'sysinit.target.wants/'],
         ['systemd-machined.service',             'ENABLE_MACHINED',
          'dbus-org.freedesktop.machine1.service'],
         ['systemd-modules-load.service',         'HAVE_KMOD',
@@ -182,6 +200,9 @@ in_units = [
         ['systemd-nspawn@.service',              ''],
         ['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-quotacheck.service',           'ENABLE_QUOTACHECK'],
         ['systemd-random-seed.service',          'ENABLE_RANDOMSEED',
          'sysinit.target.wants/'],
@@ -191,20 +212,10 @@ in_units = [
         ['systemd-suspend.service',              ''],
         ['systemd-sysctl.service',               '',
          'sysinit.target.wants/'],
-        ['systemd-sysusers.service',             'ENABLE_SYSUSERS',
-         'sysinit.target.wants/'],
         ['systemd-timedated.service',            'ENABLE_TIMEDATED',
          'dbus-org.freedesktop.timedate1.service'],
         ['systemd-timesyncd.service',            'ENABLE_TIMESYNCD'],
         ['systemd-time-wait-sync.service',       'ENABLE_TIMESYNCD'],
-        ['systemd-tmpfiles-clean.service',       'ENABLE_TMPFILES'],
-        ['systemd-tmpfiles-setup-dev.service',   'ENABLE_TMPFILES',
-         'sysinit.target.wants/'],
-        ['systemd-tmpfiles-setup.service',       'ENABLE_TMPFILES',
-         'sysinit.target.wants/'],
-        ['systemd-udev-settle.service',          ''],
-        ['systemd-udev-trigger.service',         '',
-         'sysinit.target.wants/'],
         ['systemd-udevd.service',                '',
          'sysinit.target.wants/'],
         ['systemd-update-done.service',          '',
@@ -217,6 +228,8 @@ in_units = [
          'multi-user.target.wants/'],
         ['systemd-vconsole-setup.service',       'ENABLE_VCONSOLE'],
         ['systemd-volatile-root.service',        ''],
+        ['systemd-repart.service',               'ENABLE_REPART',
+         'sysinit.target.wants/ initrd-root-fs.target.wants/'],
         ['user-runtime-dir@.service',            ''],
         ['user@.service',                        ''],
 ]
index 5dca2cb77187e249efb9f4b5f71343cdd8d50565..34a0557735f18f648105ecdc83884c65de1ce55d 100644 (file)
@@ -13,6 +13,7 @@ DefaultDependencies=no
 Before=sysinit.target
 Documentation=man:modprobe(8)
 ConditionCapability=CAP_SYS_MODULE
+ConditionPathExists=!/sys/module/%I
 
 [Service]
 Type=oneshot
diff --git a/units/sys-kernel-tracing.mount b/units/sys-kernel-tracing.mount
new file mode 100644 (file)
index 0000000..cb42b47
--- /dev/null
@@ -0,0 +1,23 @@
+#  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=Kernel Trace File System
+Documentation=https://www.kernel.org/doc/Documentation/trace/ftrace.txt
+Documentation=https://www.freedesktop.org/wiki/Software/systemd/APIFileSystems
+DefaultDependencies=no
+ConditionPathExists=/sys/kernel/tracing
+ConditionCapability=CAP_SYS_RAWIO
+Before=sysinit.target
+
+[Mount]
+What=tracefs
+Where=/sys/kernel/tracing
+Type=tracefs
+Options=nosuid,nodev,noexec
similarity index 90%
rename from units/systemd-ask-password-console.service.in
rename to units/systemd-ask-password-console.service
index 60fa7c320009cae82d764499150d3156d37fee91..6ee4c253a39dafcfe4c294072e7db97598b4f704 100644 (file)
@@ -17,5 +17,5 @@ Before=shutdown.target
 ConditionPathExists=!/run/plymouth/pid
 
 [Service]
-ExecStart=@rootbindir@/systemd-tty-ask-password-agent --watch --console
+ExecStart=systemd-tty-ask-password-agent --watch --console
 SystemCallArchitectures=native
similarity index 68%
rename from units/systemd-ask-password-wall.service.in
rename to units/systemd-ask-password-wall.service
index 1e4808b6d57fadd8a6a82a4b92a5c3e6e6bb79af..52a3037cd48ff454b2f56d07b99327778e9929ac 100644 (file)
@@ -13,6 +13,6 @@ Documentation=man:systemd-ask-password-console.service(8)
 After=systemd-user-sessions.service
 
 [Service]
-ExecStartPre=-@SYSTEMCTL@ stop systemd-ask-password-console.path systemd-ask-password-console.service systemd-ask-password-plymouth.path systemd-ask-password-plymouth.service
-ExecStart=@rootbindir@/systemd-tty-ask-password-agent --wall
+ExecStartPre=-systemctl stop systemd-ask-password-console.path systemd-ask-password-console.service systemd-ask-password-plymouth.path systemd-ask-password-plymouth.service
+ExecStart=systemd-tty-ask-password-agent --wall
 SystemCallArchitectures=native
similarity index 96%
rename from units/systemd-boot-system-token.service.in
rename to units/systemd-boot-system-token.service
index e9b742c5c7015b610c8d816e57905659fd4730a4..8aead02417824ee78e2bea41023a2a38e29344d1 100644 (file)
@@ -31,4 +31,4 @@ ConditionPathExists=|!/sys/firmware/efi/efivars/LoaderRandomSeed-4a67b082-0a4c-4
 [Service]
 Type=oneshot
 RemainAfterExit=yes
-ExecStart=@bindir@/bootctl random-seed --graceful
+ExecStart=bootctl random-seed --graceful
similarity index 87%
rename from units/systemd-firstboot.service.in
rename to units/systemd-firstboot.service
index d4deba90b7445c081827d62d227bd122a3d4d552..9f5c7101cd50d846e0978a895245b74599383231 100644 (file)
@@ -20,7 +20,7 @@ ConditionFirstBoot=yes
 [Service]
 Type=oneshot
 RemainAfterExit=yes
-ExecStart=@rootbindir@/systemd-firstboot --prompt-locale --prompt-timezone --prompt-root-password
+ExecStart=systemd-firstboot --prompt-locale --prompt-timezone --prompt-root-password
 StandardOutput=tty
 StandardInput=tty
 StandardError=tty
similarity index 93%
rename from units/systemd-halt.service.in
rename to units/systemd-halt.service
index 09c10058298de140c09380182b33c824e6cd38bd..cd16d1de202a827e0f380d94f287b6ebcfc83a58 100644 (file)
@@ -16,4 +16,4 @@ After=shutdown.target umount.target final.target
 
 [Service]
 Type=oneshot
-ExecStart=@SYSTEMCTL@ --force halt
+ExecStart=systemctl --force halt
diff --git a/units/systemd-homed.service.in b/units/systemd-homed.service.in
new file mode 100644 (file)
index 0000000..512804c
--- /dev/null
@@ -0,0 +1,36 @@
+#  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=Home Manager
+Documentation=man:systemd-homed.service(8)
+RequiresMountsFor=/home
+
+[Service]
+BusName=org.freedesktop.home1
+CapabilityBoundingSet=CAP_SYS_ADMIN CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER CAP_FSETID CAP_SETGID CAP_SETUID
+DeviceAllow=/dev/loop-control rw
+DeviceAllow=/dev/mapper/control rw
+DeviceAllow=block-* rw
+ExecStart=@rootlibexecdir@/systemd-homed
+IPAddressDeny=any
+KillMode=mixed
+LimitNOFILE=@HIGH_RLIMIT_NOFILE@
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
+PrivateNetwork=yes
+RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_ALG
+RestrictNamespaces=mnt
+RestrictRealtime=yes
+StateDirectory=systemd/home
+SystemCallArchitectures=native
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service @mount
+@SERVICE_WATCHDOG@
index 259fe0de8b01e39c2c708cc02b34ffaaf9620d24..9887018a1f23631a3ca7e2cfd0f9f8b935054fee 100644 (file)
@@ -22,5 +22,5 @@ ConditionDirectoryNotEmpty=|/etc/udev/hwdb.d/
 [Service]
 Type=oneshot
 RemainAfterExit=yes
-ExecStart=@rootbindir@/systemd-hwdb update
+ExecStart=systemd-hwdb update
 TimeoutSec=90s
similarity index 93%
rename from units/systemd-journal-catalog-update.service.in
rename to units/systemd-journal-catalog-update.service
index 18b2739ffab0bfe066937cd72c1b69d8fb205df6..6db55a5490ff89825e0321dc8d28b5321f5b1d10 100644 (file)
@@ -19,5 +19,5 @@ ConditionNeedsUpdate=/var
 [Service]
 Type=oneshot
 RemainAfterExit=yes
-ExecStart=@rootbindir@/journalctl --update-catalog
+ExecStart=journalctl --update-catalog
 TimeoutSec=90s
similarity index 87%
rename from units/systemd-journal-flush.service.in
rename to units/systemd-journal-flush.service
index 29b006cba5775ac2fb42859e4d4505d923dac776..0f45743fa0a76e9e54c41b6b46a000ab4eda9af0 100644 (file)
@@ -17,8 +17,8 @@ Before=systemd-tmpfiles-setup.service
 RequiresMountsFor=/var/log/journal
 
 [Service]
-ExecStart=@rootbindir@/journalctl --flush
-ExecStop=@rootbindir@/journalctl --smart-relinquish-var
+ExecStart=journalctl --flush
+ExecStop=journalctl --smart-relinquish-var
 Type=oneshot
 RemainAfterExit=yes
 TimeoutSec=90s
diff --git a/units/systemd-journald-varlink@.socket b/units/systemd-journald-varlink@.socket
new file mode 100644 (file)
index 0000000..b6730c2
--- /dev/null
@@ -0,0 +1,18 @@
+#  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=Journal Varlink Socket for Namespace %i
+Documentation=man:systemd-journald.service(8) man:journald.conf(5)
+StopWhenUnneeded=yes
+
+[Socket]
+Service=systemd-journald@%i.service
+ListenStream=/run/systemd/journal.%i/io.systemd.journal
+SocketMode=0600
index 303d5a4826c11ebb6d8188018c9911ed5adea137..5144868bcb711912e5b640f27b6eca8bcca3c767 100644 (file)
@@ -16,7 +16,6 @@ After=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-a
 Before=sysinit.target
 
 [Service]
-OOMScoreAdjust=-250
 CapabilityBoundingSet=CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_SYS_PTRACE CAP_SYSLOG CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_CHOWN CAP_DAC_READ_SEARCH CAP_FOWNER CAP_SETUID CAP_SETGID CAP_MAC_OVERRIDE
 DeviceAllow=char-* rw
 ExecStart=@rootlibexecdir@/systemd-journald
@@ -25,12 +24,15 @@ IPAddressDeny=any
 LockPersonality=yes
 MemoryDenyWriteExecute=yes
 NoNewPrivileges=yes
+OOMScoreAdjust=-250
 Restart=always
 RestartSec=0
 RestrictAddressFamilies=AF_UNIX AF_NETLINK
 RestrictNamespaces=yes
 RestrictRealtime=yes
 RestrictSUIDSGID=yes
+RuntimeDirectory=systemd/journal
+RuntimeDirectoryPreserve=yes
 Sockets=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-audit.socket
 StandardOutput=null
 SystemCallArchitectures=native
diff --git a/units/systemd-journald@.service.in b/units/systemd-journald@.service.in
new file mode 100644 (file)
index 0000000..e7ea919
--- /dev/null
@@ -0,0 +1,44 @@
+#  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=Journal Service for Namespace %i
+Documentation=man:systemd-journald.service(8) man:journald.conf(5)
+Requires=systemd-journald@%i.socket systemd-journald-varlink@%i.socket
+After=systemd-journald@%i.socket systemd-journald-varlink@%i.socket
+
+[Service]
+CapabilityBoundingSet=CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_SYS_PTRACE CAP_CHOWN CAP_DAC_READ_SEARCH CAP_FOWNER CAP_SETUID CAP_SETGID CAP_MAC_OVERRIDE
+DevicePolicy=closed
+ExecStart=@rootlibexecdir@/systemd-journald %i
+FileDescriptorStoreMax=4224
+Group=systemd-journal
+IPAddressDeny=any
+LockPersonality=yes
+LogsDirectory=journal/%m.%i
+LogsDirectoryMode=02755
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
+RestrictAddressFamilies=AF_UNIX AF_NETLINK
+RestrictNamespaces=yes
+RestrictRealtime=yes
+RestrictSUIDSGID=yes
+RuntimeDirectory=systemd/journal.%i
+RuntimeDirectoryPreserve=yes
+Sockets=systemd-journald@%i.socket
+StandardOutput=null
+SystemCallArchitectures=native
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service
+Type=notify
+@SERVICE_WATCHDOG@
+
+# If there are many split up journal files we need a lot of fds to access them
+# all in parallel.
+LimitNOFILE=@HIGH_RLIMIT_NOFILE@
diff --git a/units/systemd-journald@.socket b/units/systemd-journald@.socket
new file mode 100644 (file)
index 0000000..3badd78
--- /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.
+
+[Unit]
+Description=Journal Socket for Namespace %i
+Documentation=man:systemd-journald.service(8) man:journald.conf(5)
+StopWhenUnneeded=yes
+
+[Socket]
+Service=systemd-journald@%i.service
+ListenStream=/run/systemd/journal.%i/stdout
+ListenDatagram=/run/systemd/journal.%i/socket
+ListenDatagram=/run/systemd/journal.%i/dev-log
+SocketMode=0666
+PassCredentials=yes
+PassSecurity=yes
+ReceiveBuffer=8M
+SendBuffer=8M
similarity index 93%
rename from units/systemd-kexec.service.in
rename to units/systemd-kexec.service
index 1201b232895b810df5ea8f9ff54b3ebb8716b2b9..7413e1d6b1a5881c518257cc3fa7f32ce5bd80ba 100644 (file)
@@ -16,4 +16,4 @@ After=shutdown.target umount.target final.target
 
 [Service]
 Type=oneshot
-ExecStart=@SYSTEMCTL@ --force kexec
+ExecStart=systemctl --force kexec
similarity index 92%
rename from units/systemd-machine-id-commit.service.in
rename to units/systemd-machine-id-commit.service
index 4f348730eed0cca43ba85587e6f2e61f7213da34..e3acb0f3260bc30ae28d6556296c561fe145c7ec 100644 (file)
@@ -20,5 +20,5 @@ ConditionPathIsMountPoint=/etc/machine-id
 [Service]
 Type=oneshot
 RemainAfterExit=yes
-ExecStart=@rootbindir@/systemd-machine-id-setup --commit
+ExecStart=systemd-machine-id-setup --commit
 TimeoutSec=30s
index fa344d487dafe76cce70179423cae2c730850e2c..3db0281f81dcb5f8589f5cd2c49b3cc4b93c6bc9 100644 (file)
@@ -24,7 +24,6 @@ LockPersonality=yes
 MemoryDenyWriteExecute=yes
 NoNewPrivileges=yes
 ProtectHostname=yes
-ProtectKernelLogs=yes
 RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
 RestrictRealtime=yes
 SystemCallArchitectures=native
index e314aded9b326860c7c12dc332120077b70c5106..070d87e1543f88e7fc72fa724afbd5a2e27adc8a 100644 (file)
@@ -9,11 +9,13 @@
 
 [Unit]
 Description=Generate network units from Kernel command line
+Documentation=man:systemd-network-generator.service(8)
 DefaultDependencies=no
 Before=network-pre.target
 
 [Service]
 Type=oneshot
+RemainAfterExit=yes
 ExecStart=@rootlibexecdir@/systemd-network-generator
 
 [Install]
index 01931665a494bca3796c7e4215c73b74942da092..1b69677496d9e44bc5429fe4b087f087dc1bc337 100644 (file)
@@ -33,7 +33,7 @@ ProtectKernelLogs=yes
 ProtectSystem=strict
 Restart=on-failure
 RestartSec=0
-RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 AF_PACKET
+RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 AF_PACKET AF_ALG
 RestrictNamespaces=yes
 RestrictRealtime=yes
 RestrictSUIDSGID=yes
index 5367ee44105b3bf7dddd21e2773f5473bdee7a64..c91c92707e060826c9e3fa5ed751de6a4fe248ff 100644 (file)
@@ -14,11 +14,11 @@ Wants=modprobe@tun.service modprobe@loop.service modprobe@dm-mod.service
 PartOf=machines.target
 Before=machines.target
 After=network.target systemd-resolved.service modprobe@tun.service modprobe@loop.service modprobe@dm-mod.service
-RequiresMountsFor=/var/lib/machines
+RequiresMountsFor=/var/lib/machines/%i
 
 [Service]
 # Make sure the DeviceAllow= lines below can properly resolve the 'block-loop' expression (and others)
-ExecStart=@bindir@/systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --network-veth -U --settings=override --machine=%i
+ExecStart=systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --network-veth -U --settings=override --machine=%i
 KillMode=mixed
 Type=notify
 RestartForceExitStatus=133
index dde21bc33e52b1d09a3af6b6592fcc07e2f6c183..89f34afe3411ccec5dc9e053250384742516b36b 100644 (file)
@@ -11,6 +11,7 @@
 Description=Platform Persistent Storage Archival
 Documentation=man:systemd-pstore(8)
 ConditionDirectoryNotEmpty=/sys/fs/pstore
+ConditionVirtualization=!container
 DefaultDependencies=no
 Wants=systemd-remount-fs.service
 After=systemd-remount-fs.service
diff --git a/units/systemd-repart.service.in b/units/systemd-repart.service.in
new file mode 100644 (file)
index 0000000..7ce6aef
--- /dev/null
@@ -0,0 +1,25 @@
+#  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=Repartition Root Disk
+Documentation=man:systemd-repart.service(8)
+DefaultDependencies=no
+Conflicts=shutdown.target
+After=sysroot.mount
+Before=initrd-root-fs.target shutdown.target
+ConditionVirtualization=!container
+
+[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
+SuccessExitStatus=77
similarity index 94%
rename from units/systemd-sysusers.service.in
rename to units/systemd-sysusers.service
index 4d11bbb76266363151dbe329a92813df5633afcd..da05e0eb1bbe9cb07c0968086ad91c4fda9f9729 100644 (file)
@@ -19,5 +19,5 @@ ConditionNeedsUpdate=/etc
 [Service]
 Type=oneshot
 RemainAfterExit=yes
-ExecStart=@rootbindir@/systemd-sysusers
+ExecStart=systemd-sysusers
 TimeoutSec=90s
similarity index 92%
rename from units/systemd-tmpfiles-clean.service.in
rename to units/systemd-tmpfiles-clean.service
index 5d70aafb29f7cf411906b7c0925788dae00e7386..f20bb143ef7a99dded415e8cb24437fbb25fcfc9 100644 (file)
@@ -17,6 +17,6 @@ Before=shutdown.target
 
 [Service]
 Type=oneshot
-ExecStart=@rootbindir@/systemd-tmpfiles --clean
+ExecStart=systemd-tmpfiles --clean
 SuccessExitStatus=DATAERR
 IOSchedulingClass=idle
similarity index 90%
rename from units/systemd-tmpfiles-setup-dev.service.in
rename to units/systemd-tmpfiles-setup-dev.service
index ed52db4953a6c7a5a4f309520a387414d779a60d..1027823859e5974167959cf01d95b71567d6286d 100644 (file)
@@ -18,5 +18,5 @@ Before=sysinit.target local-fs-pre.target systemd-udevd.service shutdown.target
 [Service]
 Type=oneshot
 RemainAfterExit=yes
-ExecStart=@rootbindir@/systemd-tmpfiles --prefix=/dev --create --boot
+ExecStart=systemd-tmpfiles --prefix=/dev --create --boot
 SuccessExitStatus=DATAERR CANTCREAT
similarity index 89%
rename from units/systemd-tmpfiles-setup.service.in
rename to units/systemd-tmpfiles-setup.service
index 32a475d71531a2121ebecf89388014733228c1e1..29799ee81caa3cee7542f0027835a3f2fa626263 100644 (file)
@@ -19,5 +19,5 @@ RefuseManualStop=yes
 [Service]
 Type=oneshot
 RemainAfterExit=yes
-ExecStart=@rootbindir@/systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev
+ExecStart=systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev
 SuccessExitStatus=DATAERR CANTCREAT
similarity index 95%
rename from units/systemd-udev-settle.service.in
rename to units/systemd-udev-settle.service
index 22ebf08c5196d2044998f657062946b14a5c3548..ed6a68b864f2850b4134269f1a09cca9f2c412c1 100644 (file)
@@ -24,4 +24,4 @@ ConditionPathIsReadWrite=/sys
 Type=oneshot
 TimeoutSec=180
 RemainAfterExit=yes
-ExecStart=@rootbindir@/udevadm settle
+ExecStart=udevadm settle
similarity index 82%
rename from units/systemd-udev-trigger.service.in
rename to units/systemd-udev-trigger.service
index b60204eccc0fc7abcfa199d18bf927e495cffd8d..8a625b630599286874e658cb34409bb6bf2210ee 100644 (file)
@@ -19,5 +19,5 @@ ConditionPathIsReadWrite=/sys
 [Service]
 Type=oneshot
 RemainAfterExit=yes
-ExecStart=@rootbindir@/udevadm trigger --type=subsystems --action=add
-ExecStart=@rootbindir@/udevadm trigger --type=devices --action=add
+ExecStart=udevadm trigger --type=subsystems --action=add
+ExecStart=udevadm trigger --type=devices --action=add
index 8b1dd0efc73fa91877f167dd910fd29f67dd8c8e..5eee69933bde944c9afc7db3779997891ecb3a06 100644 (file)
@@ -23,7 +23,7 @@ Sockets=systemd-udevd-control.socket systemd-udevd-kernel.socket
 Restart=always
 RestartSec=0
 ExecStart=@rootlibexecdir@/systemd-udevd
-ExecReload=@rootbindir@/udevadm control --reload --timeout 0
+ExecReload=udevadm control --reload --timeout 0
 KillMode=mixed
 TasksMax=infinity
 PrivateMounts=yes
diff --git a/units/systemd-userdbd.service.in b/units/systemd-userdbd.service.in
new file mode 100644 (file)
index 0000000..e30ed21
--- /dev/null
@@ -0,0 +1,41 @@
+#  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=User Database Manager
+Documentation=man:systemd-userdbd.service(8)
+Requires=systemd-userdbd.socket
+After=systemd-userdbd.socket
+Before=sysinit.target
+DefaultDependencies=no
+
+[Service]
+CapabilityBoundingSet=CAP_DAC_READ_SEARCH
+ExecStart=@rootlibexecdir@/systemd-userdbd
+IPAddressDeny=any
+LimitNOFILE=@HIGH_RLIMIT_NOFILE@
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
+PrivateDevices=yes
+ProtectControlGroups=yes
+ProtectHome=yes
+ProtectHostname=yes
+ProtectKernelLogs=yes
+ProtectKernelModules=yes
+ProtectSystem=strict
+RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
+RestrictNamespaces=yes
+RestrictRealtime=yes
+RestrictSUIDSGID=yes
+SystemCallArchitectures=native
+SystemCallErrorNumber=EPERM
+SystemCallFilter=@system-service
+Type=notify
+@SERVICE_WATCHDOG@
diff --git a/units/systemd-userdbd.socket b/units/systemd-userdbd.socket
new file mode 100644 (file)
index 0000000..1c749ea
--- /dev/null
@@ -0,0 +1,19 @@
+#  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=User Database Manager Socket
+Documentation=man:systemd-userdbd.service(8)
+DefaultDependencies=no
+Before=sockets.target
+
+[Socket]
+ListenStream=/run/systemd/userdb/io.systemd.Multiplexer
+Symlinks=/run/systemd/userdb/io.systemd.NameServiceSwitch
+SocketMode=0666
index f4178f495ac46bee0d063ea8fcd09dd505df0e56..9042521c9d736f2a442ef62e3f44c232b80288e6 100644 (file)
@@ -16,4 +16,5 @@ ConditionPathExists=/dev/tty0
 
 [Service]
 Type=oneshot
+RemainAfterExit=yes
 ExecStart=@rootlibexecdir@/systemd-vconsole-setup
index 36341a42f5abf48a22fadd7b075a18fb49a761ed..cb8f630b8cde79b606c7ec37f8f06ff7db5024e6 100644 (file)
@@ -13,26 +13,14 @@ units = [
         'smartcard.target',
         'sockets.target',
         'sound.target',
-        'timers.target',
         'systemd-exit.service',
+        'systemd-tmpfiles-clean.service',
         'systemd-tmpfiles-clean.timer',
+        'systemd-tmpfiles-setup.service',
+        'timers.target',
 ]
 
 foreach file : units
         install_data(file,
                      install_dir : userunitdir)
 endforeach
-
-in_units = [
-        'systemd-tmpfiles-clean.service',
-        'systemd-tmpfiles-setup.service',
-]
-
-foreach file : in_units
-        gen = configure_file(
-                input : file + '.in',
-                output : file,
-                configuration : substs)
-        install_data(gen,
-                     install_dir : userunitdir)
-endforeach
similarity index 91%
rename from units/user/systemd-tmpfiles-clean.service.in
rename to units/user/systemd-tmpfiles-clean.service
index 306b064e895330e443b43af8e14407eb5cc1d691..3be0de5f7ddfcd1b4b80ef8de8c67a41e5dc4b0e 100644 (file)
@@ -16,6 +16,6 @@ Before=basic.target shutdown.target
 
 [Service]
 Type=oneshot
-ExecStart=@rootbindir@/systemd-tmpfiles --user --clean
+ExecStart=systemd-tmpfiles --user --clean
 SuccessExitStatus=DATAERR
 IOSchedulingClass=idle
similarity index 90%
rename from units/user/systemd-tmpfiles-setup.service.in
rename to units/user/systemd-tmpfiles-setup.service
index a852ef5748038647bccd69874590414bb532377f..c4b29cbe8ce381cd331e62b2ba5d19c5df47ad9b 100644 (file)
@@ -18,7 +18,7 @@ RefuseManualStop=yes
 [Service]
 Type=oneshot
 RemainAfterExit=yes
-ExecStart=@rootbindir@/systemd-tmpfiles --user --create --remove --boot
+ExecStart=systemd-tmpfiles --user --create --remove --boot
 SuccessExitStatus=DATAERR
 
 [Install]
index e898b3978308c03625ab2cd846ca9b73bb14dbd2..ace24369ddd3c6f95b3cea8f44f3fe30dec94548 100644 (file)
@@ -18,7 +18,7 @@ IgnoreOnIsolate=yes
 User=%i
 PAMName=systemd-user
 Type=notify
-ExecStart=-@rootlibexecdir@/systemd --user
+ExecStart=@rootlibexecdir@/systemd --user
 Slice=user-%i.slice
 KillMode=mixed
 Delegate=pids memory